blob: 2c260fe6bd8003acee9a81a2bcf96a447b621d13 [file] [log] [blame]
#!/usr/bin/env python
#
# Copyright 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""Tests for google.appengine.tools.devappserver2.python.sandbox."""
import __builtin__
import imp
import os
import re
import sys
import types
import urllib
import unittest
import google
try:
import lxml
except ImportError:
raise unittest.SkipTest('sandbox_test could not import lxml')
try:
import PIL
except ImportError:
raise unittest.SkipTest('sandbox_test could not import PIL')
import mox
from google.appengine.tools.devappserver2 import runtime_config_pb2
from google.appengine.tools.devappserver2.python import sandbox
from google.appengine.tools.devappserver2.python import stubs
class SandboxTest(unittest.TestCase):
def setUp(self):
super(SandboxTest, self).setUp()
self.mox = mox.Mox()
self.old_path = sys.path
self.old_meta_path = sys.meta_path
self.old_library_format_string = sandbox._THIRD_PARTY_LIBRARY_FORMAT_STRING
self.config = runtime_config_pb2.Config()
self.app_root = 'test/app/root'
self.config.application_root = self.app_root
self.config.app_id = 'app'
self.config.version_id = '1'
self.builtins = __builtin__.__dict__.copy()
self.modules = sys.modules.copy()
def tearDown(self):
sys.modules.clear()
sys.modules.update(self.modules)
__builtin__.__dict__.update(self.builtins)
sys.meta_path = self.old_meta_path
sys.path = self.old_path
sandbox._THIRD_PARTY_LIBRARY_FORMAT_STRING = self.old_library_format_string
self.mox.UnsetStubs()
super(SandboxTest, self).tearDown()
def test_enable_libraries(self):
sandbox._THIRD_PARTY_LIBRARY_FORMAT_STRING = (
'name=%(name)s-version=%(version)s')
libs = self.config.libraries
libs.add(name='foo', version='12345')
libs.add(name='webapp2', version='2.5.1')
self.assertEqual(
[os.path.join(os.path.dirname(os.path.dirname(
google.__file__)), 'name=foo-version=12345'),
os.path.join(os.path.dirname(os.path.dirname(
google.__file__)), 'name=webapp2-version=2.5.1')],
sandbox._enable_libraries(libs))
def test_enable_libraries_no_libraries(self):
libs = self.config.libraries
self.assertEqual([], sandbox._enable_libraries(libs))
self.assertEqual(self.old_path, sys.path)
class ModuleOverrideImportHookTest(unittest.TestCase):
def setUp(self):
super(ModuleOverrideImportHookTest, self).setUp()
self.test_policies = {}
self.path = sys.path[:]
self.hook = sandbox.ModuleOverrideImportHook(self.test_policies)
sys.path_importer_cache = {}
sys.modules.pop('distutils', None)
__import__('distutils').__path__.insert(0, 'dummy/path')
sys.modules.pop('distutils.util', None)
sys.modules.pop('thread', None)
self.imported_modules = set(sys.modules)
self.path_hooks = sys.path_hooks
def tearDown(self):
sys.path_hooks = self.path_hooks
sys.path_importer_cache = {}
sys.path = self.path
added_modules = set(sys.modules) - self.imported_modules
for name in added_modules:
del sys.modules[name]
distutils_modules = [module for module in sys.modules if
module.startswith('distutils')]
for name in distutils_modules:
del sys.modules[name]
sys.modules.pop('thread', None)
super(ModuleOverrideImportHookTest, self).tearDown()
def test_load_builtin_pass_through(self):
symbols = dir(__import__('thread'))
del sys.modules['thread']
self.test_policies['thread'] = sandbox.ModuleOverridePolicy(
None, [], {}, default_pass_through=True)
thread = self.hook.load_module('thread')
self.assertTrue(isinstance(thread, types.ModuleType))
self.assertTrue(isinstance(thread.__doc__, str))
self.assertItemsEqual(symbols + ['__loader__'], dir(thread))
self.assertEqual(self.hook, thread.__loader__)
def test_load_builtin_no_pass_through(self):
self.test_policies['thread'] = sandbox.ModuleOverridePolicy(
None, [], {}, default_pass_through=False)
thread = self.hook.load_module('thread')
self.assertTrue(isinstance(thread, types.ModuleType))
self.assertItemsEqual(
['__doc__', '__name__', '__package__', '__loader__'], dir(thread))
self.assertEqual(self.hook, thread.__loader__)
def test_load_with_path_hook(self):
class DummyPathHook(object):
def __init__(self, path):
if path != 'dummy/path':
raise ImportError
def find_module(self, unused_fullname):
return self
def load_module(self, fullname):
return imp.new_module('fake name: %s' % fullname)
self.test_policies['distutils.util'] = sandbox.ModuleOverridePolicy(
None, [], {}, default_pass_through=True)
sys.path_hooks = [DummyPathHook]
util = self.hook.load_module('distutils.util')
self.assertEqual('fake name: distutils.util', util.__name__)
def test_load_with_path_hook_cant_find(self):
class DummyPathHook(object):
def __init__(self, path):
if path != 'dummy/path':
raise ImportError
def find_module(self, unused_fullname):
return None
def load_module(self, fullname):
raise ImportError
self.test_policies['distutils.util'] = sandbox.ModuleOverridePolicy(
None, [], {}, default_pass_through=True)
sys.path_hooks = [DummyPathHook]
util = self.hook.load_module('distutils.util')
self.assertEqual('distutils.util', util.__name__)
def test_load_without_path_hook(self):
self.test_policies['urllib'] = sandbox.ModuleOverridePolicy(
None, [], {}, default_pass_through=True)
urllib = self.hook.load_module('urllib')
self.assertIn('urlopen', urllib.__dict__)
self.assertEqual('urllib', urllib.__name__)
def test_load_without_path_hook_not_found(self):
self.test_policies['urllib'] = sandbox.ModuleOverridePolicy(
None, [], {}, default_pass_through=True)
self.assertRaises(ImportError, self.hook.load_module, 'fake_module')
def test_load_already_in_sys_modules(self):
module = imp.new_module('foo')
sys.modules['foo'] = module
self.assertEqual(module, self.hook.load_module('foo'))
def test_is_package(self):
self.assertTrue(self.hook.is_package('distutils'))
def test_is_package_standard_lib(self):
self.assertTrue(self.hook.is_package('email'))
def test_is_package_not_package_standard_lib(self):
self.assertFalse(self.hook.is_package('urllib'))
def test_is_package_not_package(self):
self.assertFalse(self.hook.is_package('distutils.util'))
def test_is_package_does_not_exist(self):
self.assertRaises(ImportError, self.hook.is_package, 'foo.bar')
def test_get_source(self):
with open(__import__('distutils').__file__.replace('.pyc', '.py')) as f:
source = f.read()
self.assertEqual(source, self.hook.get_source('distutils'))
def test_get_source_does_not_exist(self):
self.assertRaises(ImportError, self.hook.get_source, 'foo.bar')
def test_get_source_standard_library(self):
# The expected value is hard to find if the standard library might or might
# not be zipped so just check that a value is found.
self.assertTrue(self.hook.get_source('urllib'))
def test_get_code(self):
filename = __import__('distutils').__file__.replace('.pyc', '.py')
with open(filename) as f:
expected_code = compile(f.read(), filename, 'exec')
self.assertEqual(expected_code, self.hook.get_code('distutils'))
def test_get_code_does_not_exist(self):
self.assertRaises(ImportError, self.hook.get_code, 'foo.bar')
def test_get_code_standard_library(self):
# The expected value is hard to find if the standard library might or might
# not be zipped so just check that a value is found.
self.assertTrue(self.hook.get_code('urllib'))
def test_os_module_policy(self):
hooked_os = imp.new_module('os')
hooked_os.__dict__.update(os.__dict__)
sandbox._MODULE_OVERRIDE_POLICIES['os'].apply_policy(hooked_os.__dict__)
self.assertEqual(stubs.return_minus_one, hooked_os.getpid)
self.assertNotIn('execv', hooked_os.__dict__)
self.assertEqual(stubs.os_error_not_implemented, hooked_os.unlink)
self.assertEqual(os.walk, hooked_os.walk)
class CModuleImportHookTest(unittest.TestCase):
def test_find_module_enabled_module(self):
hook = sandbox.CModuleImportHook([re.compile(r'lxml\.')])
self.assertIsNone(hook.find_module('lxml'))
lxml = __import__('lxml')
self.assertIsNone(hook.find_module('lxml.etree', lxml.__path__))
def test_find_module_disabled_module(self):
hook = sandbox.CModuleImportHook([re.compile(r'numpy\.')])
self.assertIsNone(hook.find_module('lxml'))
lxml = __import__('lxml')
self.assertEqual(hook, hook.find_module('lxml.etree', lxml.__path__))
def test_find_module_not_c_module(self):
hook = sandbox.CModuleImportHook([])
self.assertIsNone(hook.find_module('httplib'))
def test_find_module_whitelisted(self):
hook = sandbox.CModuleImportHook([])
for name in sandbox._WHITE_LIST_C_MODULES:
self.assertIsNone(hook.find_module(name))
def test_find_module_not_whitelisted(self):
hook = sandbox.CModuleImportHook([])
self.assertEqual(hook, hook.find_module('__builtin__'))
def test_find_module_not_whitelisted_enabled_via_libaries(self):
hook = sandbox.CModuleImportHook([re.compile(r'__builtin__')])
self.assertIsNone(hook.find_module('__builtin__'))
def test_load_module(self):
hook = sandbox.CModuleImportHook([])
self.assertRaises(ImportError, hook.load_module, 'lxml')
class PathOverrideImportHookTest(unittest.TestCase):
def setUp(self):
self.saved_lxml = lxml
self.saved_pil = PIL
self.saved_urllib = urllib
def tearDown(self):
sys.modules['urllib'] = self.saved_urllib
sys.modules['PIL'] = self.saved_pil
sys.modules['lxml'] = self.saved_lxml
def test_package_success(self):
hook = sandbox.PathOverrideImportHook(['lxml'])
self.assertEqual(hook, hook.find_module('lxml'))
del sys.modules['lxml']
hooked_lxml = hook.load_module('lxml')
self.assertEqual(hooked_lxml.__file__, lxml.__file__)
self.assertEqual(hooked_lxml.__path__, lxml.__path__)
self.assertEqual(hooked_lxml.__loader__, hook)
self.assertEqual([os.path.dirname(self.saved_lxml.__file__)],
hook.extra_accessible_paths)
self.assertFalse(hook.extra_sys_paths)
def test_package_success_pil_in_sys_path(self):
hook = sandbox.PathOverrideImportHook(['PIL'])
self.assertEqual(hook, hook.find_module('PIL'))
del sys.modules['PIL']
hooked_pil = hook.load_module('PIL')
self.assertEqual(hooked_pil.__file__, PIL.__file__)
self.assertEqual(hooked_pil.__path__, PIL.__path__)
self.assertEqual(hooked_pil.__loader__, hook)
self.assertFalse(hook.extra_accessible_paths)
self.assertEqual([os.path.dirname(self.saved_pil.__file__)],
hook.extra_sys_paths)
def test_module_success(self):
hook = sandbox.PathOverrideImportHook(['urllib'])
self.assertEqual(hook, hook.find_module('urllib'))
del sys.modules['urllib']
hooked_urllib = hook.load_module('urllib')
self.assertEqual(hooked_urllib.__file__.replace('.pyc', '.py'),
urllib.__file__.replace('.pyc', '.py'))
self.assertEqual(hooked_urllib.__loader__, hook)
self.assertNotIn('__path__', hooked_urllib.__dict__)
self.assertFalse(hook.extra_accessible_paths)
self.assertFalse(hook.extra_sys_paths)
def test_disabled_modules(self):
hook = sandbox.PathOverrideImportHook(['lxml'])
self.assertFalse(hook.find_module('lxml.foo'))
self.assertFalse(hook.find_module('numpy'))
self.assertFalse(hook.find_module('os'))
def test_module_not_installed(self):
hook = sandbox.PathOverrideImportHook(['foo'])
self.assertFalse(hook.find_module('foo'))
self.assertFalse(hook.extra_accessible_paths)
self.assertFalse(hook.extra_sys_paths)
def test_import_alread_in_sys_modules(self):
hook = sandbox.PathOverrideImportHook(['lxml'])
self.assertEqual(os, hook.load_module('os'))
class PathRestrictingImportHookTest(unittest.TestCase):
def setUp(self):
self.mox = mox.Mox()
self.mox.StubOutWithMock(imp, 'find_module')
self.mox.StubOutWithMock(stubs.FakeFile, 'is_file_accessible')
self.hook = sandbox.PathRestrictingImportHook([re.compile(r'lxml(\..*)?$')])
def tearDown(self):
self.mox.UnsetStubs()
def test_accessible(self):
imp.find_module('bar', ['foo']).AndReturn((None, 'foo/bar.py',
(None, None, imp.PY_SOURCE)))
stubs.FakeFile.is_file_accessible('foo/bar.py').AndReturn(True)
self.mox.ReplayAll()
self.assertIsNone(self.hook.find_module('foo.bar', ['foo']))
def test_not_accessible(self):
imp.find_module('bar', ['foo']).AndReturn((None, 'foo/bar.py',
(None, None, imp.PY_SOURCE)))
stubs.FakeFile.is_file_accessible('foo/bar.py').AndReturn(False)
self.mox.ReplayAll()
self.assertEqual(self.hook, self.hook.find_module('foo.bar', ['foo']))
def test_c_module_accessible(self):
imp.find_module('bar', ['foo']).AndReturn((None, 'foo/bar.so',
(None, None, imp.C_EXTENSION)))
stubs.FakeFile.is_file_accessible('foo/bar.so').AndReturn(True)
self.mox.ReplayAll()
self.assertIsNone(self.hook.find_module('foo.bar', ['foo']))
def test_c_module_not_accessible(self):
imp.find_module('bar', ['foo']).AndReturn((None, 'foo/bar.so',
(None, None, imp.C_EXTENSION)))
stubs.FakeFile.is_file_accessible('foo/bar.so').AndReturn(False)
self.mox.ReplayAll()
self.assertEqual(self.hook, self.hook.find_module('foo.bar', ['foo']))
def test_compiled_python_accessible(self):
imp.find_module('bar', ['foo']).AndReturn((None, 'foo/bar.pyc',
(None, None, imp.PY_COMPILED)))
stubs.FakeFile.is_file_accessible('foo/bar.pyc').AndReturn(True)
self.mox.ReplayAll()
self.assertIsNone(self.hook.find_module('foo.bar', ['foo']))
def test_compiled_python_not_accessible(self):
imp.find_module('bar', ['foo']).AndReturn((None, 'foo/bar.pyc',
(None, None, imp.PY_COMPILED)))
stubs.FakeFile.is_file_accessible('foo/bar.pyc').AndReturn(False)
self.mox.ReplayAll()
self.assertEqual(self.hook, self.hook.find_module('foo.bar', ['foo']))
def test_c_builtin(self):
imp.find_module('bar', ['foo']).AndReturn((None, 'bar',
(None, None, imp.C_BUILTIN)))
self.mox.ReplayAll()
self.assertIsNone(self.hook.find_module('foo.bar', ['foo']))
def test_py_frozen(self):
imp.find_module('bar', ['foo']).AndReturn((None, 'bar',
(None, None, imp.PY_FROZEN)))
self.mox.ReplayAll()
self.assertIsNone(self.hook.find_module('foo.bar', ['foo']))
def test_enabled_c_library(self):
imp.find_module('lxmla', ['foo']).AndReturn((None, 'lxmla.py',
(None, None, imp.PY_SOURCE)))
stubs.FakeFile.is_file_accessible('lxmla.py').AndReturn(False)
self.mox.ReplayAll()
self.assertEqual(self.hook, self.hook.find_module('lxmla', ['foo']))
self.assertIsNone(self.hook.find_module('lxml', None))
self.assertIsNone(self.hook.find_module('lxml.html', None))
def test_load_module(self):
self.assertRaises(ImportError, self.hook.load_module, 'os')
class PyCryptoRandomImportHookTest(unittest.TestCase):
def test_find_module(self):
self.assertIsInstance(
sandbox.PyCryptoRandomImportHook.find_module(
'Crypto.Random.OSRNG.posix'),
sandbox.PyCryptoRandomImportHook)
self.assertIsNone(
sandbox.PyCryptoRandomImportHook.find_module('Crypto.Random.OSRNG.nt'))
if __name__ == '__main__':
unittest.main()