3 # Copyright 2007 Google Inc.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 """A sandbox implementation that emulates production App Engine."""
32 from google
.appengine
import dist
33 from google
.appengine
.api
import app_logging
34 from google
.appengine
.api
.logservice
import logservice
35 from google
.appengine
import dist27
as dist27
36 from google
.appengine
.ext
.remote_api
import remote_api_stub
37 from google
.appengine
.runtime
import request_environment
38 from google
.appengine
.tools
.devappserver2
.python
import request_state
39 from google
.appengine
.tools
.devappserver2
.python
import stubs
41 # Needed to handle source file encoding
42 CODING_MAGIC_COMMENT_RE
= re
.compile('coding[:=]\s*([-\w.]+)')
43 DEFAULT_ENCODING
= 'ascii'
45 _C_MODULES
= frozenset(['numpy', 'Crypto', 'lxml', 'PIL'])
47 NAME_TO_CMODULE_WHITELIST_REGEX
= {
48 'numpy': re
.compile(r
'numpy(\..*)?$'),
49 'pycrypto': re
.compile(r
'Crypto(\..*)?$'),
50 'lxml': re
.compile(r
'lxml(\..*)?$'),
51 'PIL': re
.compile(r
'(PIL(\..*)?|_imaging|_imagingft|_imagingmath)$'),
54 # Maps App Engine third-party library names to the Python package name for
55 # libraries whose names differ from the package names.
56 _THIRD_PARTY_LIBRARY_NAME_OVERRIDES
= {
60 # The location of third-party libraries will be different for the packaged SDK.
61 _THIRD_PARTY_LIBRARY_FORMAT_STRING
= (
62 'lib/%(name)s-%(version)s')
64 # Store all the modules removed from sys.modules so they don't get cleaned up.
68 def _make_request_id_aware_start_new_thread(base_start_new_thread
):
69 """Returns a replacement for start_new_thread that inherits request id.
71 Returns a function with an interface that matches thread.start_new_thread
72 where the new thread inherits the request id of the current thread. The
73 request id is used by the Remote API to associate API calls with the HTTP
74 request that provoked them.
77 base_start_new_thread: The thread.start_new_thread function to call to
81 A replacement for start_new_thread.
84 def _start_new_thread(target
, args
, kw
=None):
88 request_id
= remote_api_stub
.RemoteStub
._GetRequestId
()
89 request
= request_state
.get_request_state(request_id
)
93 remote_api_stub
.RemoteStub
._SetRequestId
(request_id
)
94 request
.start_thread()
97 request_environment
.current_request
.Clear()
99 return base_start_new_thread(_run
, ())
100 return _start_new_thread
103 def enable_sandbox(config
):
104 """Enable the sandbox based on the configuration.
106 This includes installing import hooks to restrict access to C modules and
107 stub out functions that are not implemented in production, replacing the file
108 builtins with read-only versions and add enabled libraries to the path.
111 config: The runtime_config_pb2.Config to use to configure the sandbox.
114 modules
= [os
, traceback
, google
, protorpc
]
115 c_module
= _find_shared_object_c_module()
117 modules
.append(c_module
)
118 module_paths
= [module
.__file
__ for module
in modules
]
119 module_paths
.extend([os
.path
.realpath(module
.__file
__) for module
in modules
])
120 python_lib_paths
= [config
.application_root
]
121 for path
in sys
.path
:
122 if any(module_path
.startswith(path
) for module_path
in module_paths
):
123 python_lib_paths
.append(path
)
124 python_lib_paths
.extend(_enable_libraries(config
.libraries
))
125 for name
in list(sys
.modules
):
126 if not _should_keep_module(name
):
127 _removed_modules
.append(sys
.modules
[name
])
128 del sys
.modules
[name
]
129 path_override_hook
= PathOverrideImportHook(
130 set(_THIRD_PARTY_LIBRARY_NAME_OVERRIDES
.get(lib
.name
, lib
.name
)
131 for lib
in config
.libraries
).intersection(_C_MODULES
))
132 python_lib_paths
.extend(path_override_hook
.extra_sys_paths
)
133 stubs
.FakeFile
.set_allowed_paths(config
.application_root
,
134 python_lib_paths
[1:] +
135 path_override_hook
.extra_accessible_paths
)
136 stubs
.FakeFile
.set_skip_files(config
.skip_files
)
137 stubs
.FakeFile
.set_static_files(config
.static_files
)
138 __builtin__
.file = stubs
.FakeFile
139 __builtin__
.open = stubs
.FakeFile
140 types
.FileType
= stubs
.FakeFile
141 sys
.platform
= 'linux3'
142 enabled_library_regexes
= [
143 NAME_TO_CMODULE_WHITELIST_REGEX
[lib
.name
] for lib
in config
.libraries
144 if lib
.name
in NAME_TO_CMODULE_WHITELIST_REGEX
]
146 StubModuleImportHook(),
147 ModuleOverrideImportHook(_MODULE_OVERRIDE_POLICIES
),
149 CModuleImportHook(enabled_library_regexes
),
151 PyCryptoRandomImportHook
,
152 PathRestrictingImportHook(enabled_library_regexes
)
154 sys
.path_importer_cache
= {}
155 sys
.path
= python_lib_paths
[:]
157 thread
= __import__('thread')
158 __import__('%s.threading' % dist27
.__name__
)
159 threading
= sys
.modules
['%s.threading' % dist27
.__name__
]
160 thread
.start_new_thread
= _make_request_id_aware_start_new_thread(
161 thread
.start_new_thread
)
162 # This import needs to be after enabling the sandbox so it imports the
163 # sandboxed version of the logging module.
164 from google
.appengine
.runtime
import runtime
165 runtime
.PatchStartNewThread(thread
)
166 threading
._start
_new
_thread
= thread
.start_new_thread
168 os
.chdir(config
.application_root
)
169 sandboxed_os
= __import__('os')
170 request_environment
.PatchOsEnviron(sandboxed_os
)
171 os
.__dict
__.update(sandboxed_os
.__dict
__)
172 _init_logging(config
.stderr_log_level
)
175 def _find_shared_object_c_module():
176 for module_name
in ['_sqlite3', '_multiprocessing', '_ctypes', 'bz2']:
178 module
= __import__(module_name
)
182 if hasattr(module
, '__file__'):
187 def _should_keep_module(name
):
188 """Returns True if the module should be retained after sandboxing."""
189 return (name
in ('__builtin__', 'sys', 'codecs', 'encodings', 'site',
191 name
.startswith('google.') or name
.startswith('encodings.') or
193 # Making mysql available is a hack to make the CloudSQL functionality
195 'mysql' in name
.lower())
198 def _init_logging(stderr_log_level
):
199 logging
= __import__('logging')
200 logger
= logging
.getLogger()
202 console_handler
= logging
.StreamHandler(sys
.stderr
)
203 if stderr_log_level
== 0:
204 console_handler
.setLevel(logging
.DEBUG
)
205 elif stderr_log_level
== 1:
206 console_handler
.setLevel(logging
.INFO
)
207 elif stderr_log_level
== 2:
208 console_handler
.setLevel(logging
.WARNING
)
209 elif stderr_log_level
== 3:
210 console_handler
.setLevel(logging
.ERROR
)
211 elif stderr_log_level
== 4:
212 console_handler
.setLevel(logging
.CRITICAL
)
214 console_handler
.setFormatter(logging
.Formatter(
215 '%(levelname)-8s %(asctime)s %(filename)s:%(lineno)s] %(message)s'))
216 logger
.addHandler(console_handler
)
218 logging_stream
= request_environment
.RequestLocalStream(
219 request_environment
.current_request
)
220 logger
.addHandler(app_logging
.AppLogsHandler())
221 logger
.setLevel(logging
.DEBUG
)
222 logservice
.logs_buffer
= lambda: request_environment
.current_request
.errors
223 sys
.stderr
= Tee(sys
.stderr
, logging_stream
)
227 """A writeable stream that forwards to zero or more streams."""
229 def __init__(self
, *streams
):
230 self
._streams
= streams
233 for stream
in self
._streams
:
237 for stream
in self
._streams
:
240 def write(self
, data
):
241 for stream
in self
._streams
:
244 def writelines(self
, data
):
245 for stream
in self
._streams
:
246 stream
.writelines(data
)
249 def _enable_libraries(libraries
):
250 """Add enabled libraries to the path.
253 libraries: A repeated Config.Library containing the libraries to enable.
256 A list of paths containing the enabled libraries.
259 library_pattern
= os
.path
.join(os
.path
.dirname(
260 os
.path
.dirname(google
.__file
__)), _THIRD_PARTY_LIBRARY_FORMAT_STRING
)
261 for library
in libraries
:
262 library_dir
= os
.path
.abspath(
263 library_pattern
% {'name': library
.name
, 'version': library
.version
})
264 library_dirs
.append(library_dir
)
268 class BaseImportHook(object):
269 """A base class implementing common import hook functionality.
271 This provides utilities for implementing both the finder and loader parts of
272 the PEP 302 importer protocol and implements the optional extensions to the
276 def _find_module_or_loader(self
, submodule_name
, fullname
, path
):
277 """Acts like imp.find_module with support for path hooks.
280 submodule_name: The name of the submodule within its parent package.
281 fullname: The full name of the module to load.
282 path: A list containing the paths to search for the module.
285 A tuple (source_file, path_name, description, loader) where:
286 source_file: An open file or None.
287 path_name: A str containing the the path to the module.
288 description: A description tuple like the one imp.find_module returns.
289 loader: A PEP 302 compatible path hook. If this is not None, then the
290 other elements will be None.
293 ImportError: The module could not be imported.
295 for path_entry
in path
+ [None]:
296 result
= self
._find
_path
_hook
(submodule_name
, fullname
, path_entry
)
297 if result
is not None:
300 raise ImportError('No module named %s' % fullname
)
301 if isinstance(result
, tuple):
302 return result
+ (None,)
304 return None, None, None, result
.find_module(fullname
)
306 def _find_and_load_module(self
, submodule_name
, fullname
, path
):
307 """Finds and loads a module, using a provided search path.
310 submodule_name: The name of the submodule within its parent package.
311 fullname: The full name of the module to load.
312 path: A list containing the paths to search for the module.
315 The requested module.
318 ImportError: The module could not be imported.
320 source_file
, path_name
, description
, loader
= self
._find
_module
_or
_loader
(
321 submodule_name
, fullname
, path
)
323 return loader
.load_module(fullname
)
325 return imp
.load_module(fullname
, source_file
, path_name
, description
)
330 def _find_path_hook(self
, submodule
, submodule_fullname
, path_entry
):
331 """Helper for _find_and_load_module to find a module in a path entry.
334 submodule: The last portion of the module name from submodule_fullname.
335 submodule_fullname: The full name of the module to be imported.
336 path_entry: A single sys.path entry, or None representing the builtins.
339 None if nothing was found, a PEP 302 loader if one was found or a
340 tuple (source_file, path_name, description) where:
341 source_file: An open file of the source file.
342 path_name: A str containing the path to the source file.
343 description: A description tuple to be passed to imp.load_module.
345 if path_entry
is None:
346 # This is the magic entry that tells us to look for a built-in module.
347 if submodule_fullname
in sys
.builtin_module_names
:
349 result
= imp
.find_module(submodule
)
353 # Did find_module() find a built-in module? Unpack the result.
354 _
, _
, description
= result
355 _
, _
, file_type
= description
356 if file_type
== imp
.C_BUILTIN
:
358 # Skip over this entry if we get this far.
361 # It's a regular sys.path entry.
363 importer
= sys
.path_importer_cache
[path_entry
]
365 # Cache miss; try each path hook in turn.
367 for hook
in sys
.path_hooks
:
369 importer
= hook(path_entry
)
373 # This importer doesn't handle this path entry.
375 # Cache the result, whether an importer matched or not.
376 sys
.path_importer_cache
[path_entry
] = importer
379 # No importer. Use the default approach.
381 return imp
.find_module(submodule
, [path_entry
])
385 # Have an importer. Try it.
386 loader
= importer
.find_module(submodule_fullname
)
387 if loader
is not None:
388 # This importer knows about this module.
394 def _get_parent_package(self
, fullname
):
395 """Retrieves the parent package of a fully qualified module name.
398 fullname: Full name of the module whose parent should be retrieved (e.g.,
402 Module instance for the parent or None if there is no parent module.
405 ImportError: The module's parent could not be found.
407 all_modules
= fullname
.split('.')
408 parent_module_fullname
= '.'.join(all_modules
[:-1])
409 if parent_module_fullname
:
410 __import__(parent_module_fullname
)
411 return sys
.modules
[parent_module_fullname
]
414 def _get_parent_search_path(self
, fullname
):
415 """Determines the search path of a module's parent package.
418 fullname: Full name of the module to look up (e.g., foo.bar).
421 Tuple (submodule, search_path) where:
422 submodule: The last portion of the module name from fullname (e.g.,
423 if fullname is foo.bar, then this is bar).
424 search_path: List of paths that belong to the parent package's search
425 path or None if there is no parent package.
428 ImportError exception if the module or its parent could not be found.
430 _
, _
, submodule
= fullname
.rpartition('.')
431 parent_package
= self
._get
_parent
_package
(fullname
)
432 search_path
= sys
.path
433 if parent_package
is not None and hasattr(parent_package
, '__path__'):
434 search_path
= parent_package
.__path
__
435 return submodule
, search_path
437 def _get_module_info(self
, fullname
):
438 """Determines the path on disk and the search path of a module or package.
441 fullname: Full name of the module to look up (e.g., foo.bar).
444 Tuple (pathname, search_path, submodule, loader) where:
445 pathname: String containing the full path of the module on disk,
446 or None if the module wasn't loaded from disk (e.g. from a zipfile).
447 search_path: List of paths that belong to the found package's search
448 path or None if found module is not a package.
449 submodule: The relative name of the submodule that's being imported.
450 loader: A PEP 302 compatible path hook. If this is not None, then the
451 other elements will be None.
453 submodule
, search_path
= self
._get
_parent
_search
_path
(fullname
)
454 _
, pathname
, description
, loader
= self
._find
_module
_or
_loader
(
455 submodule
, fullname
, search_path
)
457 return None, None, None, loader
459 _
, _
, file_type
= description
460 module_search_path
= None
461 if file_type
== imp
.PKG_DIRECTORY
:
462 module_search_path
= [pathname
]
463 pathname
= os
.path
.join(pathname
, '__init__%spy' % os
.extsep
)
464 return pathname
, module_search_path
, submodule
, None
466 def is_package(self
, fullname
):
467 """Returns whether the module specified by fullname refers to a package.
469 This implements part of the extensions to the PEP 302 importer protocol.
472 fullname: The fullname of the module.
475 True if fullname refers to a package.
477 submodule
, search_path
= self
._get
_parent
_search
_path
(fullname
)
478 _
, _
, description
, loader
= self
._find
_module
_or
_loader
(
479 submodule
, fullname
, search_path
)
481 return loader
.is_package(fullname
)
482 _
, _
, file_type
= description
483 if file_type
== imp
.PKG_DIRECTORY
:
487 def get_source(self
, fullname
):
488 """Returns the source for the module specified by fullname.
490 This implements part of the extensions to the PEP 302 importer protocol.
493 fullname: The fullname of the module.
496 The source for the module.
498 full_path
, _
, _
, loader
= self
._get
_module
_info
(fullname
)
500 return loader
.get_source(fullname
)
501 if full_path
is None:
503 source_file
= open(full_path
)
505 return source_file
.read()
509 def get_code(self
, fullname
):
510 """Returns the code object for the module specified by fullname.
512 This implements part of the extensions to the PEP 302 importer protocol.
515 fullname: The fullname of the module.
518 The code object associated the module.
520 full_path
, _
, _
, loader
= self
._get
_module
_info
(fullname
)
522 return loader
.get_code(fullname
)
523 if full_path
is None:
525 source_file
= open(full_path
)
527 source_code
= source_file
.read()
531 # Check that coding cookie is correct if present, error if not present and
532 # we can't decode with the default of 'ascii'. According to PEP 263 this
533 # coding cookie line must be in the first or second line of the file.
534 encoding
= DEFAULT_ENCODING
535 for line
in source_code
.split('\n', 2)[:2]:
536 matches
= CODING_MAGIC_COMMENT_RE
.findall(line
)
538 encoding
= matches
[0].lower()
539 # This may raise up to the user, which is what we want, however we ignore
540 # the output because we don't want to return a unicode version of the code.
541 source_code
.decode(encoding
)
543 return compile(source_code
, full_path
, 'exec')
546 class PathOverrideImportHook(BaseImportHook
):
547 """An import hook that imports enabled modules from predetermined paths.
549 Imports handled by this hook ignore the paths in sys.path, instead using paths
550 discovered at initialization time.
553 extra_sys_paths: A list of paths that should be added to sys.path.
554 extra_accessible_paths: A list of paths that should be accessible by
558 def __init__(self
, modules
):
560 self
.extra_accessible_paths
= []
561 self
.extra_sys_paths
= []
562 for module
in modules
:
563 module_path
= self
._get
_module
_path
(module
)
565 self
._modules
[module
] = module_path
566 if isinstance(module_path
, str):
567 package_dir
= os
.path
.join(module_path
, module
)
568 if os
.path
.isdir(package_dir
):
570 self
.extra_sys_paths
.append(package_dir
)
572 self
.extra_accessible_paths
.append(package_dir
)
574 def find_module(self
, fullname
, unused_path
=None):
575 return fullname
in self
._modules
and self
or None
577 def load_module(self
, fullname
):
578 if fullname
in sys
.modules
:
579 return sys
.modules
[fullname
]
580 module_path
= self
._modules
[fullname
]
581 if hasattr(module_path
, 'load_module'):
582 module
= module_path
.load_module(fullname
)
584 module
= self
._find
_and
_load
_module
(fullname
, fullname
, [module_path
])
585 module
.__loader
__ = self
588 def _get_module_path(self
, fullname
):
589 """Returns the directory containing the module or None if not found."""
591 _
, _
, submodule
= fullname
.rpartition('.')
592 f
, filepath
, _
, loader
= self
._find
_module
_or
_loader
(
593 submodule
, fullname
, sys
.path
)
599 return loader
.find_module(fullname
)
600 return os
.path
.dirname(filepath
)
603 class ModuleOverridePolicy(object):
604 """A policy for implementing a partial whitelist for a module."""
606 def __init__(self
, default_stub
=None,
610 constant_types
=(str, int, long, BaseException
),
611 default_pass_through
=False):
612 self
.default_stub
= default_stub
613 self
.whitelist
= whitelist
or []
614 self
.overrides
= overrides
or {}
615 self
.deletes
= deletes
or []
616 self
.constant_types
= constant_types
617 self
.default_pass_through
= default_pass_through
619 def apply_policy(self
, module_dict
):
620 """Apply this policy to the provided module dict.
622 In order, one of the following will apply:
623 - Symbols in overrides are set to the override value.
624 - Symbols in deletes are removed.
625 - Whitelisted symbols and symbols with a constant type are unchanged.
626 - If a default stub is set, all other symbols are replaced by it.
627 - If default_pass_through is True, all other symbols are unchanged.
628 - If default_pass_through is False, all other symbols are removed.
631 module_dict: The module dict to be filtered.
633 for symbol
in module_dict
.keys():
634 if symbol
in self
.overrides
:
635 module_dict
[symbol
] = self
.overrides
[symbol
]
636 elif symbol
in self
.deletes
:
637 del module_dict
[symbol
]
638 elif not (symbol
in self
.whitelist
or
639 isinstance(module_dict
[symbol
], self
.constant_types
) or
640 (symbol
.startswith('__') and symbol
.endswith('__'))):
641 if self
.default_stub
:
642 module_dict
[symbol
] = self
.default_stub
643 elif not self
.default_pass_through
:
644 del module_dict
[symbol
]
646 _MODULE_OVERRIDE_POLICIES
= {
647 'os': ModuleOverridePolicy(
648 default_stub
=stubs
.os_error_not_implemented
,
649 whitelist
=['altsep', 'curdir', 'defpath', 'devnull', 'environ', 'error',
650 'fstat', 'getcwd', 'getcwdu', 'getenv', '_get_exports_list',
651 'name', 'open', 'pardir', 'path', 'pathsep', 'sep',
652 'stat_float_times', 'stat_result', 'strerror', 'sys',
655 'access': stubs
.fake_access
,
656 'listdir': stubs
.RestrictedPathFunction(os
.listdir
),
657 # Alias lstat() to stat() to match the behavior in production.
658 'lstat': stubs
.RestrictedPathFunction(os
.stat
),
659 'open': stubs
.fake_open
,
660 'stat': stubs
.RestrictedPathFunction(os
.stat
),
661 'uname': stubs
.fake_uname
,
662 'getpid': stubs
.return_minus_one
,
663 'getppid': stubs
.return_minus_one
,
664 'getpgrp': stubs
.return_minus_one
,
665 'getgid': stubs
.return_minus_one
,
666 'getegid': stubs
.return_minus_one
,
667 'geteuid': stubs
.return_minus_one
,
668 'getuid': stubs
.return_minus_one
,
669 'urandom': stubs
.fake_urandom
,
670 'system': stubs
.return_minus_one
,
672 deletes
=['execv', 'execve']),
673 'signal': ModuleOverridePolicy(overrides
={'__doc__': None}),
674 'locale': ModuleOverridePolicy(
675 overrides
={'setlocale': stubs
.fake_set_locale
},
676 default_pass_through
=True),
677 'distutils.util': ModuleOverridePolicy(
678 overrides
={'get_platform': stubs
.fake_get_platform
},
679 default_pass_through
=True),
680 # TODO: Stub out imp.find_module and friends.
684 class ModuleOverrideImportHook(BaseImportHook
):
685 """An import hook that applies a ModuleOverridePolicy to modules."""
687 def __init__(self
, policies
):
688 super(ModuleOverrideImportHook
, self
).__init
__()
689 self
.policies
= policies
691 def find_module(self
, fullname
, unused_path
=None):
692 return fullname
in self
.policies
and self
or None
694 def load_module(self
, fullname
):
695 if fullname
in sys
.modules
:
696 return sys
.modules
[fullname
]
697 parent_name
, _
, submodule_name
= fullname
.rpartition('.')
699 parent
= sys
.modules
[parent_name
]
700 path
= getattr(parent
, '__path__', sys
.path
)
704 module
= self
._find
_and
_load
_module
(submodule_name
, fullname
, path
)
705 self
.policies
[fullname
].apply_policy(module
.__dict
__)
706 module
.__loader
__ = self
707 sys
.modules
[fullname
] = module
711 class StubModuleImportHook(BaseImportHook
):
712 """An import hook that replaces entire modules with stubs."""
714 def find_module(self
, fullname
, unused_path
=None):
715 return self
if fullname
in dist27
.MODULE_OVERRIDES
else None
717 def load_module(self
, fullname
):
718 if fullname
in sys
.modules
:
719 return sys
.modules
[fullname
]
720 return self
.import_stub_module(fullname
)
722 def import_stub_module(self
, name
):
723 """Import the stub module replacement for the specified module."""
724 # Do the equivalent of
725 # ``from google.appengine.dist import <name>``.
726 providing_dist
= dist
727 # When using the Py27 runtime, modules in dist27 have priority.
728 # (They have already been vetted.)
729 if name
in dist27
.__all__
:
730 providing_dist
= dist27
731 fullname
= '%s.%s' % (providing_dist
.__name
__, name
)
732 __import__(fullname
, {}, {})
733 module
= imp
.new_module(fullname
)
734 module
.__dict
__.update(sys
.modules
[fullname
].__dict
__)
735 module
.__loader
__ = self
736 module
.__name
__ = name
737 module
.__package
__ = None
738 module
.__name
__ = name
739 sys
.modules
[name
] = module
742 _WHITE_LIST_C_MODULES
= [
757 '_collections', # Python 2.6 compatibility
780 '_md5', # Python2.5 compatibility
782 'nt', # Only indirectly through the os module.
785 'posix', # Only indirectly through the os module.
788 '_sha256', # Python2.5 compatibility
789 '_sha512', # Python2.5 compatibility
790 '_sha', # Python2.5 compatibility
807 class BuiltinImportHook(object):
808 """An import hook implementing a builtin whitelist.
810 BuiltinImportHook implements the PEP 302 finder protocol where it returns
811 itself as a loader for any builtin module that isn't whitelisted. The loader
812 implementation always raises ImportError.
815 def find_module(self
, fullname
, unused_path
=None):
816 if (fullname
in sys
.builtin_module_names
and
817 fullname
not in _WHITE_LIST_C_MODULES
):
821 def load_module(self
, fullname
):
822 raise ImportError('No module named %s' % fullname
)
825 class CModuleImportHook(object):
826 """An import hook implementing a C module whitelist.
828 CModuleImportHook implements the PEP 302 finder protocol where it returns
829 itself as a loader for any builtin module that isn't whitelisted or part of an
830 enabled third-party library. The loader implementation always raises
834 def __init__(self
, enabled_regexes
):
835 self
._enabled
_regexes
= enabled_regexes
837 def find_module(self
, fullname
, path
=None):
838 if fullname
in _WHITE_LIST_C_MODULES
:
840 if any(regex
.match(fullname
) for regex
in self
._enabled
_regexes
):
842 _
, _
, submodule_name
= fullname
.rpartition('.')
844 result
= imp
.find_module(submodule_name
, path
)
847 f
, _
, description
= result
848 _
, _
, file_type
= description
849 if isinstance(f
, file):
851 if file_type
== imp
.C_EXTENSION
:
855 def load_module(self
, fullname
):
856 raise ImportError('No module named %s' % fullname
)
859 class PathRestrictingImportHook(object):
860 """An import hook that restricts imports to accessible paths.
862 This import hook uses FakeFile.is_file_accessible to determine which paths are
865 _EXCLUDED_TYPES
= frozenset([
870 def __init__(self
, enabled_regexes
):
871 self
._enabled
_regexes
= enabled_regexes
873 def find_module(self
, fullname
, path
=None):
874 if any(regex
.match(fullname
) for regex
in self
._enabled
_regexes
):
876 _
, _
, submodule_name
= fullname
.rpartition('.')
878 f
, filename
, description
= imp
.find_module(submodule_name
, path
)
883 _
, _
, file_type
= description
884 if (file_type
in self
._EXCLUDED
_TYPES
or
885 stubs
.FakeFile
.is_file_accessible(filename
) or
886 (filename
.endswith('.pyc') and
887 os
.path
.exists(filename
.replace('.pyc', '.py')))):
891 def load_module(self
, fullname
):
892 raise ImportError('No module named %s' % fullname
)
895 class PyCryptoRandomImportHook(BaseImportHook
):
896 """An import hook that allows Crypto.Random.OSRNG.new() to work on posix.
898 This changes PyCrypto to always use os.urandom() instead of reading from
902 def __init__(self
, path
):
906 def find_module(cls
, fullname
, path
=None):
907 if fullname
== 'Crypto.Random.OSRNG.posix':
911 def load_module(self
, fullname
):
912 if fullname
in sys
.modules
:
913 return sys
.modules
[fullname
]
914 __import__('Crypto.Random.OSRNG.fallback')
915 module
= self
._find
_and
_load
_module
('posix', fullname
, self
._path
)
916 fallback
= sys
.modules
['Crypto.Random.OSRNG.fallback']
917 module
.new
= fallback
.new
918 module
.__loader
__ = self
919 sys
.modules
[fullname
] = module