]> git.rkrishnan.org Git - tahoe-lafs/zfec.git/blob - zfec/setuptools-0.6c16dev3.egg/setuptools/sandbox.py
stick a .gitignore file
[tahoe-lafs/zfec.git] / zfec / setuptools-0.6c16dev3.egg / setuptools / sandbox.py
1 import os, sys, __builtin__, tempfile, operator, pkg_resources
2 _os = sys.modules[os.name]
3 _open = open
4 _file = file
5
6 from distutils.errors import DistutilsError
7 from pkg_resources import working_set
8
9 __all__ = [
10     "AbstractSandbox", "DirectorySandbox", "SandboxViolation", "run_setup",
11 ]
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 def run_setup(setup_script, args):
43     """Run a distutils setup script, sandboxed in its directory"""
44     old_dir = os.getcwd()
45     save_argv = sys.argv[:]
46     save_path = sys.path[:]
47     setup_dir = os.path.abspath(os.path.dirname(setup_script))
48     temp_dir = os.path.join(setup_dir,'temp')
49     if not os.path.isdir(temp_dir): os.makedirs(temp_dir)
50     save_tmp = tempfile.tempdir
51     save_modules = sys.modules.copy()
52     pr_state = pkg_resources.__getstate__()
53     try:
54         tempfile.tempdir = temp_dir; os.chdir(setup_dir)
55         try:
56             sys.argv[:] = [setup_script]+list(args)
57             sys.path.insert(0, setup_dir)
58             # reset to include setup dir, w/clean callback list
59             working_set.__init__()  
60             working_set.callbacks.append(lambda dist:dist.activate())
61             DirectorySandbox(setup_dir).run(
62                 lambda: execfile(
63                     "setup.py",
64                     {'__file__':setup_script, '__name__':'__main__'}
65                 )
66             )
67         except SystemExit, v:
68             if v.args and v.args[0]:
69                 raise
70             # Normal exit, just return
71     finally:
72         pkg_resources.__setstate__(pr_state)
73         sys.modules.update(save_modules)
74         for key in list(sys.modules):
75             if key not in save_modules: del sys.modules[key]
76         os.chdir(old_dir)
77         sys.path[:] = save_path
78         sys.argv[:] = save_argv
79         tempfile.tempdir = save_tmp
80
81
82
83 class AbstractSandbox:
84     """Wrap 'os' module and 'open()' builtin for virtualizing setup scripts"""
85
86     _active = False
87
88     def __init__(self):
89         self._attrs = [
90             name for name in dir(_os)
91                 if not name.startswith('_') and hasattr(self,name)
92         ]
93
94     def _copy(self, source):
95         for name in self._attrs:
96             setattr(os, name, getattr(source,name))
97
98     def run(self, func):
99         """Run 'func' under os sandboxing"""
100         try:
101             self._copy(self)
102             __builtin__.file = self._file
103             __builtin__.open = self._open
104             self._active = True
105             return func()
106         finally:
107             self._active = False
108             __builtin__.open = _open
109             __builtin__.file = _file
110             self._copy(_os)
111
112     def _mk_dual_path_wrapper(name):
113         original = getattr(_os,name)
114         def wrap(self,src,dst,*args,**kw):
115             if self._active:
116                 src,dst = self._remap_pair(name,src,dst,*args,**kw)
117             return original(src,dst,*args,**kw)
118         return wrap
119
120     for name in ["rename", "link", "symlink"]:
121         if hasattr(_os,name): locals()[name] = _mk_dual_path_wrapper(name)
122
123
124     def _mk_single_path_wrapper(name, original=None):
125         original = original or getattr(_os,name)
126         def wrap(self,path,*args,**kw):
127             if self._active:
128                 path = self._remap_input(name,path,*args,**kw)
129             return original(path,*args,**kw)
130         return wrap
131
132     _open = _mk_single_path_wrapper('open', _open)
133     _file = _mk_single_path_wrapper('file', _file)
134     for name in [
135         "stat", "listdir", "chdir", "open", "chmod", "chown", "mkdir",
136         "remove", "unlink", "rmdir", "utime", "lchown", "chroot", "lstat",
137         "startfile", "mkfifo", "mknod", "pathconf", "access"
138     ]:
139         if hasattr(_os,name): locals()[name] = _mk_single_path_wrapper(name)
140
141     def _mk_single_with_return(name):
142         original = getattr(_os,name)
143         def wrap(self,path,*args,**kw):
144             if self._active:
145                 path = self._remap_input(name,path,*args,**kw)
146                 return self._remap_output(name, original(path,*args,**kw))
147             return original(path,*args,**kw)
148         return wrap
149
150     for name in ['readlink', 'tempnam']:
151         if hasattr(_os,name): locals()[name] = _mk_single_with_return(name)
152
153     def _mk_query(name):
154         original = getattr(_os,name)
155         def wrap(self,*args,**kw):
156             retval = original(*args,**kw)
157             if self._active:
158                 return self._remap_output(name, retval)
159             return retval
160         return wrap
161
162     for name in ['getcwd', 'tmpnam']:
163         if hasattr(_os,name): locals()[name] = _mk_query(name)
164
165     def _validate_path(self,path):
166         """Called to remap or validate any path, whether input or output"""
167         return path
168
169     def _remap_input(self,operation,path,*args,**kw):
170         """Called for path inputs"""
171         return self._validate_path(path)
172
173     def _remap_output(self,operation,path):
174         """Called for path outputs"""
175         return self._validate_path(path)
176
177     def _remap_pair(self,operation,src,dst,*args,**kw):
178         """Called for path pairs like rename, link, and symlink operations"""
179         return (
180             self._remap_input(operation+'-from',src,*args,**kw),
181             self._remap_input(operation+'-to',dst,*args,**kw)
182         )
183
184
185 class DirectorySandbox(AbstractSandbox):
186     """Restrict operations to a single subdirectory - pseudo-chroot"""
187
188     write_ops = dict.fromkeys([
189         "open", "chmod", "chown", "mkdir", "remove", "unlink", "rmdir",
190         "utime", "lchown", "chroot", "mkfifo", "mknod", "tempnam",
191     ])
192
193     def __init__(self,sandbox):
194         self._sandbox = os.path.normcase(os.path.realpath(sandbox))
195         self._prefix = os.path.join(self._sandbox,'')
196         AbstractSandbox.__init__(self)
197
198     def _violation(self, operation, *args, **kw):
199         raise SandboxViolation(operation, args, kw)
200
201     def _open(self, path, mode='r', *args, **kw):
202         if mode not in ('r', 'rt', 'rb', 'rU', 'U') and not self._ok(path):
203             self._violation("open", path, mode, *args, **kw)
204         return _open(path,mode,*args,**kw)
205
206     def tmpnam(self): self._violation("tmpnam")
207
208     def _ok(self,path):
209         if hasattr(_os,'devnull') and path==_os.devnull: return True
210         active = self._active
211         try:
212             self._active = False
213             realpath = os.path.normcase(os.path.realpath(path))
214             if realpath==self._sandbox or realpath.startswith(self._prefix):
215                 return True
216         finally:
217             self._active = active
218
219     def _remap_input(self,operation,path,*args,**kw):
220         """Called for path inputs"""
221         if operation in self.write_ops and not self._ok(path):
222             self._violation(operation, os.path.realpath(path), *args, **kw)
223         return path
224
225     def _remap_pair(self,operation,src,dst,*args,**kw):
226         """Called for path pairs like rename, link, and symlink operations"""
227         if not self._ok(src) or not self._ok(dst):
228             self._violation(operation, src, dst, *args, **kw)
229         return (src,dst)
230
231     def _file(self, path, mode='r', *args, **kw):
232         if mode not in ('r', 'rt', 'rb', 'rU', 'U') and not self._ok(path):
233             self._violation("file", path, mode, *args, **kw)
234         return _file(path,mode,*args,**kw)
235
236     def open(self, file, flags, mode=0777):
237         """Called for low-level os.open()"""
238         if flags & WRITE_FLAGS and not self._ok(file):
239             self._violation("os.open", file, flags, mode)
240         return _os.open(file,flags,mode)
241
242 WRITE_FLAGS = reduce(
243     operator.or_, [getattr(_os, a, 0) for a in
244         "O_WRONLY O_RDWR O_APPEND O_CREAT O_TRUNC O_TEMPORARY".split()]
245 )
246
247 class SandboxViolation(DistutilsError):
248     """A setup script attempted to modify the filesystem outside the sandbox"""
249
250     def __str__(self):
251         return """SandboxViolation: %s%r %s
252
253 The package setup script has attempted to modify files on your system
254 that are not within the EasyInstall build area, and has been aborted.
255
256 This package cannot be safely installed by EasyInstall, and may not
257 support alternate installation locations even if you run its setup
258 script by hand.  Please inform the package's author and the EasyInstall
259 maintainers to find out if a fix or workaround is available.""" % self.args
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287 #