|
| 1 | +""" |
| 2 | +create errno-specific classes for IO or os calls. |
| 3 | +
|
| 4 | +""" |
| 5 | +from types import ModuleType |
| 6 | +import sys, os, errno |
| 7 | + |
| 8 | +class Error(EnvironmentError): |
| 9 | + def __repr__(self): |
| 10 | + return "%s.%s %r: %s " %(self.__class__.__module__, |
| 11 | + self.__class__.__name__, |
| 12 | + self.__class__.__doc__, |
| 13 | + " ".join(map(str, self.args)), |
| 14 | + #repr(self.args) |
| 15 | + ) |
| 16 | + |
| 17 | + def __str__(self): |
| 18 | + s = "[%s]: %s" %(self.__class__.__doc__, |
| 19 | + " ".join(map(str, self.args)), |
| 20 | + ) |
| 21 | + return s |
| 22 | + |
| 23 | +_winerrnomap = { |
| 24 | + 2: errno.ENOENT, |
| 25 | + 3: errno.ENOENT, |
| 26 | + 17: errno.EEXIST, |
| 27 | + 18: errno.EXDEV, |
| 28 | + 13: errno.EBUSY, # empty cd drive, but ENOMEDIUM seems unavailiable |
| 29 | + 22: errno.ENOTDIR, |
| 30 | + 20: errno.ENOTDIR, |
| 31 | + 267: errno.ENOTDIR, |
| 32 | + 5: errno.EACCES, # anything better? |
| 33 | +} |
| 34 | + |
| 35 | +class ErrorMaker(ModuleType): |
| 36 | + """ lazily provides Exception classes for each possible POSIX errno |
| 37 | + (as defined per the 'errno' module). All such instances |
| 38 | + subclass EnvironmentError. |
| 39 | + """ |
| 40 | + Error = Error |
| 41 | + _errno2class = {} |
| 42 | + |
| 43 | + def __getattr__(self, name): |
| 44 | + if name[0] == "_": |
| 45 | + raise AttributeError(name) |
| 46 | + eno = getattr(errno, name) |
| 47 | + cls = self._geterrnoclass(eno) |
| 48 | + setattr(self, name, cls) |
| 49 | + return cls |
| 50 | + |
| 51 | + def _geterrnoclass(self, eno): |
| 52 | + try: |
| 53 | + return self._errno2class[eno] |
| 54 | + except KeyError: |
| 55 | + clsname = errno.errorcode.get(eno, "UnknownErrno%d" %(eno,)) |
| 56 | + errorcls = type(Error)(clsname, (Error,), |
| 57 | + {'__module__':'py.error', |
| 58 | + '__doc__': os.strerror(eno)}) |
| 59 | + self._errno2class[eno] = errorcls |
| 60 | + return errorcls |
| 61 | + |
| 62 | + def checked_call(self, func, *args, **kwargs): |
| 63 | + """ call a function and raise an errno-exception if applicable. """ |
| 64 | + __tracebackhide__ = True |
| 65 | + try: |
| 66 | + return func(*args, **kwargs) |
| 67 | + except self.Error: |
| 68 | + raise |
| 69 | + except (OSError, EnvironmentError): |
| 70 | + cls, value, tb = sys.exc_info() |
| 71 | + if not hasattr(value, 'errno'): |
| 72 | + raise |
| 73 | + __tracebackhide__ = False |
| 74 | + errno = value.errno |
| 75 | + try: |
| 76 | + if not isinstance(value, WindowsError): |
| 77 | + raise NameError |
| 78 | + except NameError: |
| 79 | + # we are not on Windows, or we got a proper OSError |
| 80 | + cls = self._geterrnoclass(errno) |
| 81 | + else: |
| 82 | + try: |
| 83 | + cls = self._geterrnoclass(_winerrnomap[errno]) |
| 84 | + except KeyError: |
| 85 | + raise value |
| 86 | + raise cls("%s%r" % (func.__name__, args)) |
| 87 | + __tracebackhide__ = True |
| 88 | + |
| 89 | + |
| 90 | +error = ErrorMaker('_pytest._py.error') |
| 91 | +sys.modules[error.__name__] = error |
0 commit comments