text: """
  Internal module for the plugin system,
  the API is exposed via __init__.py
  """
  from future.builtins import object, int
  from threading import Lock
  from django.contrib.contenttypes.models import ContentType
  from fluent_pages.models import UrlNode
  from fluent_utils.load import import_apps_submodule
  from .pagetypebase import PageTypePlugin
  from six import itervalues, iteritems
  
  
  __all__ = (
      'PageTypeAlreadyRegistered', 'PageTypeNotFound', 'PageTypePool', 'page_type_pool'
  )
  
  
  class PageTypeAlreadyRegistered(Exception):
      """
      Raised when attempting to register a plugin twice.
      """
      pass
  
  
  class PageTypeNotFound(Exception):
      """
      Raised when the plugin could not be found in the rendering process.
      """
      pass
  
  
  class PageTypePool(object):
      """
      The central administration of plugins.
      """
      scanLock = Lock()
  
      def __init__(self):
          self.plugins = {}
          self._name_for_model = {}
          self._name_for_ctype_id = None
          self.detected = False
          self._file_types = None
          self._folder_types = None
          self._url_types = None
  
      def register(self, plugin):
          """
          Make a page type plugin known by the CMS.
  
          :param plugin: The plugin class, deriving from :class:`PageTypePlugin`.
  
          The plugin will be instantiated, just like Django does this with :class:`~django.contrib.admin.ModelAdmin` classes.
          If a plugin is already registered, this will raise a :class:`PluginAlreadyRegistered` exception.
          """
          # Duct-Typing does not suffice here, avoid hard to debug problems by upfront checks.
          assert issubclass(plugin, PageTypePlugin), "The plugin must inherit from `PageTypePlugin`"
          assert plugin.model, "The plugin has no model defined"
          assert issubclass(plugin.model, UrlNode), "The plugin model must inherit from `UrlNode` or `Page`."
  
          name = plugin.__name__
          if name in self.plugins:
              raise PageTypeAlreadyRegistered("[%s] a plugin with this name is already registered" % name)
  
          # Reset some caches
          self._folder_types = None
          self._file_types = None
          self._url_types = None
  
          # Make a single static instance, similar to ModelAdmin.
          plugin_instance = plugin()
          self.plugins[name] = plugin_instance
          self._name_for_model[plugin.model] = name       # Track reverse for rendering
  
          # Only update lazy indexes if already created
          if self._name_for_ctype_id is not None:
              self._name_for_ctype_id[plugin_instance.type_id] = name
  
          return plugin  # Allow class decorator syntax
  
      def get_plugins(self):
          """
          Return the :class:`PageTypePlugin` instances which are loaded.
          """
          self._import_plugins()
          return list(self.plugins.values())
  
      def get_model_classes(self):
          """
          Return all model classes which are exposed by page types.
          Each model derives from :class:`~fluent_pages.models.Page` .
          """
          self._import_plugins()
          return [plugin.model for plugin in itervalues(self.plugins)]
  
      def get_plugin_by_model(self, model_class):
          """
          Return the corresponding :class:`PageTypePlugin` for a given model.
          """
          self._import_plugins()                   # could happen during rendering that no plugin scan happened yet.
          assert issubclass(model_class, UrlNode)  # avoid confusion between model instance and class here!
  
          try:
              name = self._name_for_model[model_class]
          except KeyError:
              raise PageTypeNotFound("No plugin found for model '{0}'.".format(model_class.__name__))
          return self.plugins[name]
  
      def _get_plugin_by_content_type(self, contenttype):
          self._import_plugins()
          self._setup_lazy_indexes()
  
          ct_id = contenttype.id if isinstance(contenttype, ContentType) else int(contenttype)
          try:
              name = self._name_for_ctype_id[ct_id]
          except KeyError:
              # ContentType not found, likely a plugin is no longer registered or the app has been removed.
              try:
                  # ContentType could be stale
                  ct = contenttype if isinstance(contenttype, ContentType) else ContentType.objects.get_for_id(ct_id)
              except AttributeError:  # should return the stale type but Django <1.6 raises an AttributeError in fact.
                  ct_name = 'stale content type'
              else:
                  ct_name = '{0}.{1}'.format(ct.app_label, ct.model)
              raise PageTypeNotFound("No plugin found for content type #{0} ({1}).".format(contenttype, ct_name))
  
          return self.plugins[name]
  
      def _setup_lazy_indexes(self):
          if self._name_for_ctype_id is None:
              self._name_for_ctype_id = {}
              for name, plugin_instance in iteritems(self.plugins):
                  self._name_for_ctype_id[plugin_instance.type_id] = name
  
      def get_file_types(self):
          """
          Return the :class:`~django.contrib.contenttypes.models.ContentType` id's
          of page types that act like files (no slash or children).
          """
          if self._file_types is None:
              ct_ids = []
              for plugin in self.get_plugins():
                  if plugin.is_file:
                      ct_ids.append(plugin.type_id)
              self._file_types = ct_ids  # file_types is reset during plugin scan.
  
          return self._file_types
  
      def get_folder_types(self):
          """
          Return the :class:`~django.contrib.contenttypes.models.ContentType` id's
          of page types that operate as a container for sub pages.
          """
          if self._folder_types is None:
              ct_ids = []
              for plugin in self.get_plugins():
                  if plugin.can_have_children and not plugin.is_file:
                      ct_ids.append(plugin.type_id)
              self._folder_types = ct_ids  # folder_types is reset during plugin scan.
  
          return self._folder_types
  
      def get_url_pattern_types(self):
          """
          Return the :class:`~django.contrib.contenttypes.models.ContentType` id's
          of page types that provide URL patterns.
          """
          if self._url_types is None:
              self._url_types = [plugin.type_id for plugin in self.get_url_pattern_plugins()]
  
          return self._url_types
  
      def get_url_pattern_plugins(self):
          """
          Return the :class:`PageTypePlugin` instances that provide URL patterns.
          """
          plugins = []
          for plugin in self.get_plugins():
              if plugin.urls is not None:
                  plugins.append(plugin)
          return plugins
  
      def _import_plugins(self):
          """
          Internal function, ensure all plugin packages are imported.
          """
          if self.detected:
              return
  
          # In some cases, plugin scanning may start during a request.
          # Make sure there is only one thread scanning for plugins.
          self.scanLock.acquire()
          if self.detected:
              return  # previous threaded released + completed
  
          try:
              import_apps_submodule("page_type_plugins")
              self.detected = True
          finally:
              self.scanLock.release()
  
  
  #: The global plugin pool, a instance of the :class:`PageTypePool` class.
  page_type_pool = PageTypePool()
  