text: # 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(object):
      """
      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 = dict([
                  (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)
  