Mercurial > repos > shellac > guppy_basecaller
comparison env/lib/python3.7/site-packages/setuptools/sandbox.py @ 0:26e78fe6e8c4 draft
"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
author | shellac |
---|---|
date | Sat, 02 May 2020 07:14:21 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:26e78fe6e8c4 |
---|---|
1 import os | |
2 import sys | |
3 import tempfile | |
4 import operator | |
5 import functools | |
6 import itertools | |
7 import re | |
8 import contextlib | |
9 import pickle | |
10 import textwrap | |
11 | |
12 from setuptools.extern import six | |
13 from setuptools.extern.six.moves import builtins, map | |
14 | |
15 import pkg_resources.py31compat | |
16 | |
17 if sys.platform.startswith('java'): | |
18 import org.python.modules.posix.PosixModule as _os | |
19 else: | |
20 _os = sys.modules[os.name] | |
21 try: | |
22 _file = file | |
23 except NameError: | |
24 _file = None | |
25 _open = open | |
26 from distutils.errors import DistutilsError | |
27 from pkg_resources import working_set | |
28 | |
29 | |
30 __all__ = [ | |
31 "AbstractSandbox", "DirectorySandbox", "SandboxViolation", "run_setup", | |
32 ] | |
33 | |
34 | |
35 def _execfile(filename, globals, locals=None): | |
36 """ | |
37 Python 3 implementation of execfile. | |
38 """ | |
39 mode = 'rb' | |
40 with open(filename, mode) as stream: | |
41 script = stream.read() | |
42 if locals is None: | |
43 locals = globals | |
44 code = compile(script, filename, 'exec') | |
45 exec(code, globals, locals) | |
46 | |
47 | |
48 @contextlib.contextmanager | |
49 def save_argv(repl=None): | |
50 saved = sys.argv[:] | |
51 if repl is not None: | |
52 sys.argv[:] = repl | |
53 try: | |
54 yield saved | |
55 finally: | |
56 sys.argv[:] = saved | |
57 | |
58 | |
59 @contextlib.contextmanager | |
60 def save_path(): | |
61 saved = sys.path[:] | |
62 try: | |
63 yield saved | |
64 finally: | |
65 sys.path[:] = saved | |
66 | |
67 | |
68 @contextlib.contextmanager | |
69 def override_temp(replacement): | |
70 """ | |
71 Monkey-patch tempfile.tempdir with replacement, ensuring it exists | |
72 """ | |
73 pkg_resources.py31compat.makedirs(replacement, exist_ok=True) | |
74 | |
75 saved = tempfile.tempdir | |
76 | |
77 tempfile.tempdir = replacement | |
78 | |
79 try: | |
80 yield | |
81 finally: | |
82 tempfile.tempdir = saved | |
83 | |
84 | |
85 @contextlib.contextmanager | |
86 def pushd(target): | |
87 saved = os.getcwd() | |
88 os.chdir(target) | |
89 try: | |
90 yield saved | |
91 finally: | |
92 os.chdir(saved) | |
93 | |
94 | |
95 class UnpickleableException(Exception): | |
96 """ | |
97 An exception representing another Exception that could not be pickled. | |
98 """ | |
99 | |
100 @staticmethod | |
101 def dump(type, exc): | |
102 """ | |
103 Always return a dumped (pickled) type and exc. If exc can't be pickled, | |
104 wrap it in UnpickleableException first. | |
105 """ | |
106 try: | |
107 return pickle.dumps(type), pickle.dumps(exc) | |
108 except Exception: | |
109 # get UnpickleableException inside the sandbox | |
110 from setuptools.sandbox import UnpickleableException as cls | |
111 return cls.dump(cls, cls(repr(exc))) | |
112 | |
113 | |
114 class ExceptionSaver: | |
115 """ | |
116 A Context Manager that will save an exception, serialized, and restore it | |
117 later. | |
118 """ | |
119 | |
120 def __enter__(self): | |
121 return self | |
122 | |
123 def __exit__(self, type, exc, tb): | |
124 if not exc: | |
125 return | |
126 | |
127 # dump the exception | |
128 self._saved = UnpickleableException.dump(type, exc) | |
129 self._tb = tb | |
130 | |
131 # suppress the exception | |
132 return True | |
133 | |
134 def resume(self): | |
135 "restore and re-raise any exception" | |
136 | |
137 if '_saved' not in vars(self): | |
138 return | |
139 | |
140 type, exc = map(pickle.loads, self._saved) | |
141 six.reraise(type, exc, self._tb) | |
142 | |
143 | |
144 @contextlib.contextmanager | |
145 def save_modules(): | |
146 """ | |
147 Context in which imported modules are saved. | |
148 | |
149 Translates exceptions internal to the context into the equivalent exception | |
150 outside the context. | |
151 """ | |
152 saved = sys.modules.copy() | |
153 with ExceptionSaver() as saved_exc: | |
154 yield saved | |
155 | |
156 sys.modules.update(saved) | |
157 # remove any modules imported since | |
158 del_modules = ( | |
159 mod_name for mod_name in sys.modules | |
160 if mod_name not in saved | |
161 # exclude any encodings modules. See #285 | |
162 and not mod_name.startswith('encodings.') | |
163 ) | |
164 _clear_modules(del_modules) | |
165 | |
166 saved_exc.resume() | |
167 | |
168 | |
169 def _clear_modules(module_names): | |
170 for mod_name in list(module_names): | |
171 del sys.modules[mod_name] | |
172 | |
173 | |
174 @contextlib.contextmanager | |
175 def save_pkg_resources_state(): | |
176 saved = pkg_resources.__getstate__() | |
177 try: | |
178 yield saved | |
179 finally: | |
180 pkg_resources.__setstate__(saved) | |
181 | |
182 | |
183 @contextlib.contextmanager | |
184 def setup_context(setup_dir): | |
185 temp_dir = os.path.join(setup_dir, 'temp') | |
186 with save_pkg_resources_state(): | |
187 with save_modules(): | |
188 hide_setuptools() | |
189 with save_path(): | |
190 with save_argv(): | |
191 with override_temp(temp_dir): | |
192 with pushd(setup_dir): | |
193 # ensure setuptools commands are available | |
194 __import__('setuptools') | |
195 yield | |
196 | |
197 | |
198 def _needs_hiding(mod_name): | |
199 """ | |
200 >>> _needs_hiding('setuptools') | |
201 True | |
202 >>> _needs_hiding('pkg_resources') | |
203 True | |
204 >>> _needs_hiding('setuptools_plugin') | |
205 False | |
206 >>> _needs_hiding('setuptools.__init__') | |
207 True | |
208 >>> _needs_hiding('distutils') | |
209 True | |
210 >>> _needs_hiding('os') | |
211 False | |
212 >>> _needs_hiding('Cython') | |
213 True | |
214 """ | |
215 pattern = re.compile(r'(setuptools|pkg_resources|distutils|Cython)(\.|$)') | |
216 return bool(pattern.match(mod_name)) | |
217 | |
218 | |
219 def hide_setuptools(): | |
220 """ | |
221 Remove references to setuptools' modules from sys.modules to allow the | |
222 invocation to import the most appropriate setuptools. This technique is | |
223 necessary to avoid issues such as #315 where setuptools upgrading itself | |
224 would fail to find a function declared in the metadata. | |
225 """ | |
226 modules = filter(_needs_hiding, sys.modules) | |
227 _clear_modules(modules) | |
228 | |
229 | |
230 def run_setup(setup_script, args): | |
231 """Run a distutils setup script, sandboxed in its directory""" | |
232 setup_dir = os.path.abspath(os.path.dirname(setup_script)) | |
233 with setup_context(setup_dir): | |
234 try: | |
235 sys.argv[:] = [setup_script] + list(args) | |
236 sys.path.insert(0, setup_dir) | |
237 # reset to include setup dir, w/clean callback list | |
238 working_set.__init__() | |
239 working_set.callbacks.append(lambda dist: dist.activate()) | |
240 | |
241 # __file__ should be a byte string on Python 2 (#712) | |
242 dunder_file = ( | |
243 setup_script | |
244 if isinstance(setup_script, str) else | |
245 setup_script.encode(sys.getfilesystemencoding()) | |
246 ) | |
247 | |
248 with DirectorySandbox(setup_dir): | |
249 ns = dict(__file__=dunder_file, __name__='__main__') | |
250 _execfile(setup_script, ns) | |
251 except SystemExit as v: | |
252 if v.args and v.args[0]: | |
253 raise | |
254 # Normal exit, just return | |
255 | |
256 | |
257 class AbstractSandbox: | |
258 """Wrap 'os' module and 'open()' builtin for virtualizing setup scripts""" | |
259 | |
260 _active = False | |
261 | |
262 def __init__(self): | |
263 self._attrs = [ | |
264 name for name in dir(_os) | |
265 if not name.startswith('_') and hasattr(self, name) | |
266 ] | |
267 | |
268 def _copy(self, source): | |
269 for name in self._attrs: | |
270 setattr(os, name, getattr(source, name)) | |
271 | |
272 def __enter__(self): | |
273 self._copy(self) | |
274 if _file: | |
275 builtins.file = self._file | |
276 builtins.open = self._open | |
277 self._active = True | |
278 | |
279 def __exit__(self, exc_type, exc_value, traceback): | |
280 self._active = False | |
281 if _file: | |
282 builtins.file = _file | |
283 builtins.open = _open | |
284 self._copy(_os) | |
285 | |
286 def run(self, func): | |
287 """Run 'func' under os sandboxing""" | |
288 with self: | |
289 return func() | |
290 | |
291 def _mk_dual_path_wrapper(name): | |
292 original = getattr(_os, name) | |
293 | |
294 def wrap(self, src, dst, *args, **kw): | |
295 if self._active: | |
296 src, dst = self._remap_pair(name, src, dst, *args, **kw) | |
297 return original(src, dst, *args, **kw) | |
298 | |
299 return wrap | |
300 | |
301 for name in ["rename", "link", "symlink"]: | |
302 if hasattr(_os, name): | |
303 locals()[name] = _mk_dual_path_wrapper(name) | |
304 | |
305 def _mk_single_path_wrapper(name, original=None): | |
306 original = original or getattr(_os, name) | |
307 | |
308 def wrap(self, path, *args, **kw): | |
309 if self._active: | |
310 path = self._remap_input(name, path, *args, **kw) | |
311 return original(path, *args, **kw) | |
312 | |
313 return wrap | |
314 | |
315 if _file: | |
316 _file = _mk_single_path_wrapper('file', _file) | |
317 _open = _mk_single_path_wrapper('open', _open) | |
318 for name in [ | |
319 "stat", "listdir", "chdir", "open", "chmod", "chown", "mkdir", | |
320 "remove", "unlink", "rmdir", "utime", "lchown", "chroot", "lstat", | |
321 "startfile", "mkfifo", "mknod", "pathconf", "access" | |
322 ]: | |
323 if hasattr(_os, name): | |
324 locals()[name] = _mk_single_path_wrapper(name) | |
325 | |
326 def _mk_single_with_return(name): | |
327 original = getattr(_os, name) | |
328 | |
329 def wrap(self, path, *args, **kw): | |
330 if self._active: | |
331 path = self._remap_input(name, path, *args, **kw) | |
332 return self._remap_output(name, original(path, *args, **kw)) | |
333 return original(path, *args, **kw) | |
334 | |
335 return wrap | |
336 | |
337 for name in ['readlink', 'tempnam']: | |
338 if hasattr(_os, name): | |
339 locals()[name] = _mk_single_with_return(name) | |
340 | |
341 def _mk_query(name): | |
342 original = getattr(_os, name) | |
343 | |
344 def wrap(self, *args, **kw): | |
345 retval = original(*args, **kw) | |
346 if self._active: | |
347 return self._remap_output(name, retval) | |
348 return retval | |
349 | |
350 return wrap | |
351 | |
352 for name in ['getcwd', 'tmpnam']: | |
353 if hasattr(_os, name): | |
354 locals()[name] = _mk_query(name) | |
355 | |
356 def _validate_path(self, path): | |
357 """Called to remap or validate any path, whether input or output""" | |
358 return path | |
359 | |
360 def _remap_input(self, operation, path, *args, **kw): | |
361 """Called for path inputs""" | |
362 return self._validate_path(path) | |
363 | |
364 def _remap_output(self, operation, path): | |
365 """Called for path outputs""" | |
366 return self._validate_path(path) | |
367 | |
368 def _remap_pair(self, operation, src, dst, *args, **kw): | |
369 """Called for path pairs like rename, link, and symlink operations""" | |
370 return ( | |
371 self._remap_input(operation + '-from', src, *args, **kw), | |
372 self._remap_input(operation + '-to', dst, *args, **kw) | |
373 ) | |
374 | |
375 | |
376 if hasattr(os, 'devnull'): | |
377 _EXCEPTIONS = [os.devnull,] | |
378 else: | |
379 _EXCEPTIONS = [] | |
380 | |
381 | |
382 class DirectorySandbox(AbstractSandbox): | |
383 """Restrict operations to a single subdirectory - pseudo-chroot""" | |
384 | |
385 write_ops = dict.fromkeys([ | |
386 "open", "chmod", "chown", "mkdir", "remove", "unlink", "rmdir", | |
387 "utime", "lchown", "chroot", "mkfifo", "mknod", "tempnam", | |
388 ]) | |
389 | |
390 _exception_patterns = [ | |
391 # Allow lib2to3 to attempt to save a pickled grammar object (#121) | |
392 r'.*lib2to3.*\.pickle$', | |
393 ] | |
394 "exempt writing to paths that match the pattern" | |
395 | |
396 def __init__(self, sandbox, exceptions=_EXCEPTIONS): | |
397 self._sandbox = os.path.normcase(os.path.realpath(sandbox)) | |
398 self._prefix = os.path.join(self._sandbox, '') | |
399 self._exceptions = [ | |
400 os.path.normcase(os.path.realpath(path)) | |
401 for path in exceptions | |
402 ] | |
403 AbstractSandbox.__init__(self) | |
404 | |
405 def _violation(self, operation, *args, **kw): | |
406 from setuptools.sandbox import SandboxViolation | |
407 raise SandboxViolation(operation, args, kw) | |
408 | |
409 if _file: | |
410 | |
411 def _file(self, path, mode='r', *args, **kw): | |
412 if mode not in ('r', 'rt', 'rb', 'rU', 'U') and not self._ok(path): | |
413 self._violation("file", path, mode, *args, **kw) | |
414 return _file(path, mode, *args, **kw) | |
415 | |
416 def _open(self, path, mode='r', *args, **kw): | |
417 if mode not in ('r', 'rt', 'rb', 'rU', 'U') and not self._ok(path): | |
418 self._violation("open", path, mode, *args, **kw) | |
419 return _open(path, mode, *args, **kw) | |
420 | |
421 def tmpnam(self): | |
422 self._violation("tmpnam") | |
423 | |
424 def _ok(self, path): | |
425 active = self._active | |
426 try: | |
427 self._active = False | |
428 realpath = os.path.normcase(os.path.realpath(path)) | |
429 return ( | |
430 self._exempted(realpath) | |
431 or realpath == self._sandbox | |
432 or realpath.startswith(self._prefix) | |
433 ) | |
434 finally: | |
435 self._active = active | |
436 | |
437 def _exempted(self, filepath): | |
438 start_matches = ( | |
439 filepath.startswith(exception) | |
440 for exception in self._exceptions | |
441 ) | |
442 pattern_matches = ( | |
443 re.match(pattern, filepath) | |
444 for pattern in self._exception_patterns | |
445 ) | |
446 candidates = itertools.chain(start_matches, pattern_matches) | |
447 return any(candidates) | |
448 | |
449 def _remap_input(self, operation, path, *args, **kw): | |
450 """Called for path inputs""" | |
451 if operation in self.write_ops and not self._ok(path): | |
452 self._violation(operation, os.path.realpath(path), *args, **kw) | |
453 return path | |
454 | |
455 def _remap_pair(self, operation, src, dst, *args, **kw): | |
456 """Called for path pairs like rename, link, and symlink operations""" | |
457 if not self._ok(src) or not self._ok(dst): | |
458 self._violation(operation, src, dst, *args, **kw) | |
459 return (src, dst) | |
460 | |
461 def open(self, file, flags, mode=0o777, *args, **kw): | |
462 """Called for low-level os.open()""" | |
463 if flags & WRITE_FLAGS and not self._ok(file): | |
464 self._violation("os.open", file, flags, mode, *args, **kw) | |
465 return _os.open(file, flags, mode, *args, **kw) | |
466 | |
467 | |
468 WRITE_FLAGS = functools.reduce( | |
469 operator.or_, [getattr(_os, a, 0) for a in | |
470 "O_WRONLY O_RDWR O_APPEND O_CREAT O_TRUNC O_TEMPORARY".split()] | |
471 ) | |
472 | |
473 | |
474 class SandboxViolation(DistutilsError): | |
475 """A setup script attempted to modify the filesystem outside the sandbox""" | |
476 | |
477 tmpl = textwrap.dedent(""" | |
478 SandboxViolation: {cmd}{args!r} {kwargs} | |
479 | |
480 The package setup script has attempted to modify files on your system | |
481 that are not within the EasyInstall build area, and has been aborted. | |
482 | |
483 This package cannot be safely installed by EasyInstall, and may not | |
484 support alternate installation locations even if you run its setup | |
485 script by hand. Please inform the package's author and the EasyInstall | |
486 maintainers to find out if a fix or workaround is available. | |
487 """).lstrip() | |
488 | |
489 def __str__(self): | |
490 cmd, args, kwargs = self.args | |
491 return self.tmpl.format(**locals()) |