170 lines
6.1 KiB
Python
170 lines
6.1 KiB
Python
|
# Copyright (c) Twisted Matrix Laboratories.
|
||
|
# See LICENSE for details.
|
||
|
|
||
|
"""
|
||
|
Tests for miscellaneous behaviors of the top-level L{twisted} package (ie, for
|
||
|
the code in C{twisted/__init__.py}.
|
||
|
"""
|
||
|
|
||
|
|
||
|
import sys
|
||
|
from types import ModuleType
|
||
|
|
||
|
from twisted.trial.unittest import TestCase
|
||
|
|
||
|
|
||
|
# This is somewhat generally useful and should probably be part of a public API
|
||
|
# somewhere. See #5977.
|
||
|
class SetAsideModule:
|
||
|
"""
|
||
|
L{SetAsideModule} is a context manager for temporarily removing a module
|
||
|
from C{sys.modules}.
|
||
|
|
||
|
@ivar name: The name of the module to remove.
|
||
|
"""
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
|
||
|
def _unimport(self, name):
|
||
|
"""
|
||
|
Find the given module and all of its hierarchically inferior modules in
|
||
|
C{sys.modules}, remove them from it, and return whatever was found.
|
||
|
"""
|
||
|
modules = {
|
||
|
moduleName: module
|
||
|
for (moduleName, module) in list(sys.modules.items())
|
||
|
if (moduleName == self.name or moduleName.startswith(self.name + "."))
|
||
|
}
|
||
|
for name in modules:
|
||
|
del sys.modules[name]
|
||
|
return modules
|
||
|
|
||
|
def __enter__(self):
|
||
|
self.modules = self._unimport(self.name)
|
||
|
|
||
|
def __exit__(self, excType, excValue, traceback):
|
||
|
self._unimport(self.name)
|
||
|
sys.modules.update(self.modules)
|
||
|
|
||
|
|
||
|
def _install(modules):
|
||
|
"""
|
||
|
Take a mapping defining a package and turn it into real C{ModuleType}
|
||
|
instances in C{sys.modules}.
|
||
|
|
||
|
Consider these example::
|
||
|
|
||
|
a = {"foo": "bar"}
|
||
|
b = {"twisted": {"__version__": "42.6"}}
|
||
|
c = {"twisted": {"plugin": {"getPlugins": stub}}}
|
||
|
|
||
|
C{_install(a)} will place an item into C{sys.modules} with C{"foo"} as the
|
||
|
key and C{"bar" as the value.
|
||
|
|
||
|
C{_install(b)} will place an item into C{sys.modules} with C{"twisted"} as
|
||
|
the key. The value will be a new module object. The module will have a
|
||
|
C{"__version__"} attribute with C{"42.6"} as the value.
|
||
|
|
||
|
C{_install(c)} will place an item into C{sys.modules} with C{"twisted"} as
|
||
|
the key. The value will be a new module object with a C{"plugin"}
|
||
|
attribute. An item will also be placed into C{sys.modules} with the key
|
||
|
C{"twisted.plugin"} which refers to that module object. That module will
|
||
|
have an attribute C{"getPlugins"} with a value of C{stub}.
|
||
|
|
||
|
@param modules: A mapping from names to definitions of modules. The names
|
||
|
are native strings like C{"twisted"} or C{"unittest"}. Values may be
|
||
|
arbitrary objects. Any value which is not a dictionary will be added to
|
||
|
C{sys.modules} unmodified. Any dictionary value indicates the value is
|
||
|
a new module and its items define the attributes of that module. The
|
||
|
definition of this structure is recursive, so a value in the dictionary
|
||
|
may be a dictionary to trigger another level of processing.
|
||
|
|
||
|
@return: L{None}
|
||
|
"""
|
||
|
result = {}
|
||
|
_makePackages(None, modules, result)
|
||
|
sys.modules.update(result)
|
||
|
|
||
|
|
||
|
def _makePackages(parent, attributes, result):
|
||
|
"""
|
||
|
Construct module objects (for either modules or packages).
|
||
|
|
||
|
@param parent: L{None} or a module object which is the Python package
|
||
|
containing all of the modules being created by this function call. Its
|
||
|
name will be prepended to the name of all created modules.
|
||
|
|
||
|
@param attributes: A mapping giving the attributes of the particular module
|
||
|
object this call is creating.
|
||
|
|
||
|
@param result: A mapping which is populated with all created module names.
|
||
|
This is suitable for use in updating C{sys.modules}.
|
||
|
|
||
|
@return: A mapping of all of the attributes created by this call. This is
|
||
|
suitable for populating the dictionary of C{parent}.
|
||
|
|
||
|
@see: L{_install}.
|
||
|
"""
|
||
|
attrs = {}
|
||
|
for (name, value) in list(attributes.items()):
|
||
|
if parent is None:
|
||
|
if isinstance(value, dict):
|
||
|
module = ModuleType(name)
|
||
|
module.__dict__.update(_makePackages(module, value, result))
|
||
|
result[name] = module
|
||
|
else:
|
||
|
result[name] = value
|
||
|
else:
|
||
|
if isinstance(value, dict):
|
||
|
module = ModuleType(parent.__name__ + "." + name)
|
||
|
module.__dict__.update(_makePackages(module, value, result))
|
||
|
result[parent.__name__ + "." + name] = module
|
||
|
attrs[name] = module
|
||
|
else:
|
||
|
attrs[name] = value
|
||
|
return attrs
|
||
|
|
||
|
|
||
|
class MakePackagesTests(TestCase):
|
||
|
"""
|
||
|
Tests for L{_makePackages}, a helper for populating C{sys.modules} with
|
||
|
fictional modules.
|
||
|
"""
|
||
|
|
||
|
def test_nonModule(self):
|
||
|
"""
|
||
|
A non-C{dict} value in the attributes dictionary passed to L{_makePackages}
|
||
|
is preserved unchanged in the return value.
|
||
|
"""
|
||
|
modules = {}
|
||
|
_makePackages(None, dict(reactor="reactor"), modules)
|
||
|
self.assertEqual(modules, dict(reactor="reactor"))
|
||
|
|
||
|
def test_moduleWithAttribute(self):
|
||
|
"""
|
||
|
A C{dict} value in the attributes dictionary passed to L{_makePackages}
|
||
|
is turned into a L{ModuleType} instance with attributes populated from
|
||
|
the items of that C{dict} value.
|
||
|
"""
|
||
|
modules = {}
|
||
|
_makePackages(None, dict(twisted=dict(version="123")), modules)
|
||
|
self.assertIsInstance(modules, dict)
|
||
|
self.assertIsInstance(modules["twisted"], ModuleType)
|
||
|
self.assertEqual("twisted", modules["twisted"].__name__)
|
||
|
self.assertEqual("123", modules["twisted"].version)
|
||
|
|
||
|
def test_packageWithModule(self):
|
||
|
"""
|
||
|
Processing of the attributes dictionary is recursive, so a C{dict} value
|
||
|
it contains may itself contain a C{dict} value to the same effect.
|
||
|
"""
|
||
|
modules = {}
|
||
|
_makePackages(None, dict(twisted=dict(web=dict(version="321"))), modules)
|
||
|
self.assertIsInstance(modules, dict)
|
||
|
self.assertIsInstance(modules["twisted"], ModuleType)
|
||
|
self.assertEqual("twisted", modules["twisted"].__name__)
|
||
|
self.assertIsInstance(modules["twisted"].web, ModuleType)
|
||
|
self.assertEqual("twisted.web", modules["twisted"].web.__name__)
|
||
|
self.assertEqual("321", modules["twisted"].web.version)
|