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