1"""
2create errno-specific classes for IO or os calls.
3
4"""
5from types import ModuleType
6import sys, os, errno
7
8class 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
35class 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
90error = ErrorMaker('py.error')
91sys.modules[error.__name__] = error