[Checkins] SVN: grok/trunk/src/grok/ - got rid of package resources (doing filesystem operations directly now)

Christian Theune ct at gocept.com
Tue Oct 17 13:04:11 EDT 2006


Log message for revision 70758:
   - got rid of package resources (doing filesystem operations directly now)
   - reimplemented package and module scanning with ModuleInfo class to hide
     various detection algorithms from the grokking code
  

Changed:
  U   grok/trunk/src/grok/_grok.py
  U   grok/trunk/src/grok/scan.py
  U   grok/trunk/src/grok/tests/scan/scan.py
  U   grok/trunk/src/grok/tests/scan/stoneage/__init__.py
  U   grok/trunk/src/grok/tests/view/dirandinlinetemplate.py
  U   grok/trunk/src/grok/tests/view/dirtemplatesonly.py

-=-
Modified: grok/trunk/src/grok/_grok.py
===================================================================
--- grok/trunk/src/grok/_grok.py	2006-10-17 16:43:30 UTC (rev 70757)
+++ grok/trunk/src/grok/_grok.py	2006-10-17 17:04:10 UTC (rev 70758)
@@ -15,7 +15,6 @@
 """
 import os
 import sys
-from pkg_resources import resource_listdir, resource_exists, resource_string
 
 import persistent
 from zope import component
@@ -96,20 +95,24 @@
                              adapts=(Model, IBrowserRequest),
                              provides=IDefaultViewName)
 
-    package_or_module = resolve(dotted_name)
-    for name in scan.modules(dotted_name, package_or_module.__file__):
-        grok_module(name)
+    module_info = scan.module_info_from_dotted_name(dotted_name)
+    grok_tree(module_info)
 
-def grok_module(dotted_name):
-    module = resolve(dotted_name)
 
-    models, adapters, multiadapters, views, templates, subscribers = \
-            scan_module(dotted_name, module)
+def grok_tree(module_info):
+    grok_module(module_info)
 
-    find_filesystem_templates(dotted_name, module, templates)
+    for sub_module_info in module_info.getSubModuleInfos():
+        grok_tree(sub_module_info)
 
-    context = determine_module_context(module, models)
 
+def grok_module(module_info):
+    models, adapters, multiadapters, views, templates, subscribers = scan_module(module_info)
+
+    find_filesystem_templates(module_info, templates)
+
+    context = determine_module_context(module_info, models)
+
     register_models(models)
     register_adapters(context, adapters)
     register_multiadapters(multiadapters)
@@ -117,18 +120,19 @@
     register_unassociated_templates(context, templates)
     register_subscribers(subscribers)
 
-def scan_module(dotted_name, module):
+def scan_module(module_info):
     models = []
     adapters = []
     multiadapters = []
     views = []
     templates = TemplateRegistry()
-    subscribers = getattr(module, '__grok_subscribers__', [])
+    subscribers = module_info.getAnnotation('grok.subscribers', [])
 
+    module = module_info.getModule()
     for name in dir(module):
         obj = getattr(module, name)
 
-        if not defined_locally(obj, dotted_name):
+        if not defined_locally(obj, module_info.dotted_name):
             continue
 
         if util.check_subclass(obj, Model):
@@ -142,42 +146,42 @@
         elif isinstance(obj, PageTemplate):
             templates.register(name, obj)
             obj.__grok_name__ = name
-            obj.__grok_location__ = dotted_name
+            obj.__grok_location__ = module_info.dotted_name
 
     return models, adapters, multiadapters, views, templates, subscribers
 
-def find_filesystem_templates(dotted_name, module, templates):
-    module_name = dotted_name.split('.')[-1]
-    directory_name = directive_annotation(module, 'grok.templatedir',
-                                          module_name)
-    if resource_exists(dotted_name, directory_name):
-        template_files = resource_listdir(dotted_name, directory_name)
+def find_filesystem_templates(module_info, templates):
+    template_dir_name = module_info.getAnnotation('grok.templatedir', module_info.name)
+    template_dir = module_info.getResourcePath(template_dir_name)
+    if os.path.isdir(template_dir):
+        template_files = os.listdir(template_dir)
         for template_file in template_files:
             if template_file.startswith('.') or template_file.endswith('~'):
                 continue
 
-            template_name = template_file[:-3]
-            template_path = os.path.join(directory_name, template_file)
-
             if not template_file.endswith('.pt'):
                 raise GrokError("Unrecognized file '%s' in template directory "
-                                "'%s'."  % (template_file, directory_name),
-                                module)
+                                "'%s'." % (template_file, template_dir),
+                                module_info.getModule())
 
-            contents = resource_string(dotted_name, template_path)
+            template_name = template_file[:-3] # cut off .pt
+            template_path = os.path.join(template_dir, template_file)
+
+            f = open(template_path, 'rb')
+            contents = f.read()
+            f.close()
+
             template = PageTemplate(contents)
             template.__grok_name__ = template_name
-            # XXX is this zip-safe?
-            template.__grok_location__ = os.path.join(
-                os.path.dirname(module.__file__), template_path)
+            template.__grok_location__ = template_path
 
             inline_template = templates.get(template_name)
             if inline_template:
                 raise GrokError("Conflicting templates found for name '%s' "
                                 "in module %r, both inline and in template "
                                 "directory '%s'."
-                                % (template_name, module, directory_name),
-                                inline_template)
+                                % (template_name, module_info.getModule(),
+                                   template_dir), inline_template)
             templates.register(template_name, template)
 
 def register_static_resources(dotted_name, package_directory):
@@ -200,12 +204,12 @@
 def register_adapters(context, adapters):
     for factory in adapters:
         adapter_context = determine_class_context(factory, context)
-        name = directive_annotation(factory, 'grok.name', '')
+        name = class_annotation(factory, 'grok.name', '')
         component.provideAdapter(factory, adapts=(adapter_context,), name=name)
 
 def register_multiadapters(multiadapters):
     for factory in multiadapters:
-        name = directive_annotation(factory, 'grok.name', '')
+        name = class_annotation(factory, 'grok.name', '')
         component.provideAdapter(factory, name=name)
 
 def register_views(context, views, templates):
@@ -214,7 +218,7 @@
         factory_name = factory.__name__.lower()
 
         # find inline templates
-        template_name = directive_annotation(factory, 'grok.template',
+        template_name = class_annotation(factory, 'grok.template',
                                              factory_name)
         template = templates.get(template_name)
 
@@ -242,7 +246,7 @@
                                 "'render' method." % factory,
                                 factory)
 
-        view_name = directive_annotation(factory, 'grok.name', factory_name)
+        view_name = class_annotation(factory, 'grok.name', factory_name)
         component.provideAdapter(factory,
                                  adapts=(view_context, IDefaultBrowserLayer),
                                  provides=interface.Interface,
@@ -310,7 +314,7 @@
         raise GrokError("Multiple possible contexts for %r, please use "
                         "grok.context." % component, component)
 
-def determine_module_context(module, models):
+def determine_module_context(module_info, models):
     if len(models) == 0:
         context = None
     elif len(models) == 1:
@@ -318,18 +322,18 @@
     else:
         context = AMBIGUOUS_CONTEXT
 
-    module_context = directive_annotation(module, 'grok.context', None)
+    module_context = module_info.getAnnotation('grok.context', None)
     if module_context:
         context = module_context
 
     return context
-    
+
 def determine_class_context(class_, module_context):
-    context = directive_annotation(class_, 'grok.context', module_context)
+    context = class_annotation(class_, 'grok.context', module_context)
     check_context(class_, context)
     return context
 
-def directive_annotation(obj, name, default):
+def class_annotation(obj, name, default):
     return getattr(obj, '__%s__' % name.replace('.', '_'), default)
 
 def caller_module():

Modified: grok/trunk/src/grok/scan.py
===================================================================
--- grok/trunk/src/grok/scan.py	2006-10-17 16:43:30 UTC (rev 70757)
+++ grok/trunk/src/grok/scan.py	2006-10-17 17:04:10 UTC (rev 70758)
@@ -15,43 +15,88 @@
 """
 
 import os
-import setuptools
-from glob import glob
+
 from zope.dottedname.resolve import resolve
 
-#TODO: not ZIP-safe
-def modules(dotted_name, module_path):
-    yield dotted_name
 
-    if not (module_path.endswith('__init__.py')
-            or module_path.endswith('__init__.pyc')):
-        return
+def is_package(path):
+    if not os.path.isdir(path):
+        return False
+    init_py = os.path.join(path, '__init__.py')
+    init_pyc = init_py + 'c'
+    # Check whether either __init__.py or __init__.pyc exist
+    return os.path.isfile(init_py) or os.path.isfile(init_pyc)
 
-    package_directory = os.path.dirname(module_path)
-    seen = []
-    for entry in sorted(os.listdir(package_directory)):
-        entry_path = os.path.join(package_directory, entry)
 
-        if entry in ['__init__.py', '__init__.pyc']:
-            continue
-        elif os.path.isfile(entry_path) and (entry.endswith('.py')
-                                        or entry.endswith('.pyc')):
-            module_name = os.path.splitext(entry)[0]
-            if module_name in seen:
-                continue
-            seen.append(module_name)
-            yield '%s.%s' % (dotted_name, module_name)
-        else:
-            if os.path.isdir(entry_path):
-                init_py = os.path.join(entry_path, '__init__.py')
-                init_pyc = os.path.join(entry_path, '__init__.pyc')
+class ModuleInfo(object):
 
-                if os.path.exists(init_py):
-                    init_path = init_py
-                elif os.path.exists(init_pyc):
-                    init_path = init_pyc
-                else:
+    def __init__(self, path, dotted_name):
+        # Normalize .pyc files to .py
+        if path.endswith('c'):
+            path = path[:-1]
+        self.path = path
+        self.dotted_name = dotted_name
+        self.name = dotted_name.split('.')[-1]
+        self._module = None
+
+    def getResourcePath(self, name):
+        """Get the absolute path of a resource directory 'relative' to this
+        package or module.
+
+        Case one: get the resource directory with name <name> from the same
+        directory as this module
+
+        Case two: get the resource directory with name <name> from the children
+        of this package
+        """
+        return os.path.join(os.path.dirname(self.path), name)
+
+    def getSubModuleInfos(self):
+        if not self.isPackage():
+            return []
+        directory = os.path.dirname(self.path)
+        module_infos = []
+        seen = []
+        for entry in sorted(os.listdir(directory)):
+            entry_path = os.path.join(directory, entry)
+            name, ext = os.path.splitext(entry)
+            dotted_name = self.dotted_name + '.' + name
+
+            # Case one: modules
+            if (os.path.isfile(entry_path) and ext in ['.py', '.pyc']):
+                if name == '__init__':
                     continue
+                # Avoid duplicates when both .py and .pyc exist
+                if name in seen:
+                    continue
+                seen.append(name)
+                module_infos.append(ModuleInfo(entry_path, dotted_name))
+            # Case two: packages
+            elif is_package(entry_path):
+                # We can blindly use __init__.py even if only __init__.pyc exists
+                # because we never actually use that filename.
+                module_infos.append(ModuleInfo(
+                    os.path.join(entry_path, '__init__.py'), dotted_name))
+        return module_infos
 
-                for name in modules('%s.%s' % (dotted_name, entry), init_path):
-                    yield name
+    def getAnnotation(self, key, default):
+        key = key.replace('.', '_')
+        key = '__%s__' % key
+        module = self.getModule()
+        return getattr(module, key, default)
+
+    def getModule(self):
+        if self._module is None:
+            self._module = resolve(self.dotted_name)
+        return self._module
+
+    def isPackage(self):
+        return self.path.endswith('__init__.py')
+
+    def __repr__(self):
+        return "<ModuleInfo object for '%s'>" % self.dotted_name
+
+
+def module_info_from_dotted_name(dotted_name):
+    module = resolve(dotted_name)
+    return ModuleInfo(module.__file__, dotted_name)

Modified: grok/trunk/src/grok/tests/scan/scan.py
===================================================================
--- grok/trunk/src/grok/tests/scan/scan.py	2006-10-17 16:43:30 UTC (rev 70757)
+++ grok/trunk/src/grok/tests/scan/scan.py	2006-10-17 17:04:10 UTC (rev 70758)
@@ -1,19 +1,66 @@
 """
-  >>> import grok.scan
 
-If you call grok.scan.modules() on a module, it yields the module name:
+  >>> from grok.scan import ModuleInfo, module_info_from_dotted_name
+  
+  >>> module_info = module_info_from_dotted_name('grok.tests.scan.stoneage')
+  >>> module_info
+  <ModuleInfo object for 'grok.tests.scan.stoneage'>
+  >>> module_info.isPackage()
+  True
+  >>> module_info.dotted_name
+  'grok.tests.scan.stoneage'
+  >>> module_info.name
+  'stoneage'
 
-  >>> from grok.tests.scan.stoneage import cave
-  >>> list(grok.scan.modules('grok.tests.scan.stoneage.cave', cave.__file__))
-  ['grok.tests.scan.stoneage.cave']
+  >>> module = module_info.getModule()
+  >>> module
+  <module 'grok.tests.scan.stoneage' from '...__init__.py...'>
 
-If you call it on a package, it yield a list of the names of the
-package, its modules, and all its subpackages and their modules.
+  >>> module.__grok_foobar__ = 'GROK LOVE FOO'
+  >>> module_info.getAnnotation('grok.foobar', None)
+  'GROK LOVE FOO'
+  >>> module_info.getAnnotation('grok.barfoo', 42)
+  42
 
-  >>> from grok.tests.scan import stoneage
-  >>> list(grok.scan.modules('grok.tests.scan.stoneage', stoneage.__file__))
-  ['grok.tests.scan.stoneage', 'grok.tests.scan.stoneage.cave',
-  'grok.tests.scan.stoneage.hunt', 'grok.tests.scan.stoneage.hunt.mammoth',
-  'grok.tests.scan.stoneage.painting']
-  
+  >>> sub_modules = module_info.getSubModuleInfos()
+  >>> sub_modules
+  [<ModuleInfo object for 'grok.tests.scan.stoneage.cave'>,
+   <ModuleInfo object for 'grok.tests.scan.stoneage.hunt'>,
+   <ModuleInfo object for 'grok.tests.scan.stoneage.painting'>]
+  >>> cave_module_info = sub_modules[0]
+
+Module-level specifics
+----------------------
+
+cave is a module, not a package.
+
+  >>> cave_module_info.isPackage()
+  False
+  >>> cave_module_info.dotted_name
+  'grok.tests.scan.stoneage.cave'
+  >>> cave_module_info.name
+  'cave'
+  >>> cave_module_info.getSubModuleInfos()
+  []
+
+Resource paths
+--------------
+
+For packages, a resource path will be a child of the package directory:
+
+  >>> import os.path
+  >>> expected_resource_path = os.path.join(os.path.dirname(
+  ...     module.__file__), 'stoneage-templates')
+  >>> resource_path = module_info.getResourcePath('stoneage-templates')
+  >>> resource_path == expected_resource_path
+  True
+
+For modules, a resource path will be a sibling of the module's file:
+
+  >>> expected_resource_path = os.path.join(os.path.dirname(
+  ...     cave_module_info.getModule().__file__), 'cave-templates')
+  >>> resource_path = cave_module_info.getResourcePath('cave-templates')
+  >>> resource_path == expected_resource_path
+  True
+
 """

Modified: grok/trunk/src/grok/tests/scan/stoneage/__init__.py
===================================================================
--- grok/trunk/src/grok/tests/scan/stoneage/__init__.py	2006-10-17 16:43:30 UTC (rev 70757)
+++ grok/trunk/src/grok/tests/scan/stoneage/__init__.py	2006-10-17 17:04:10 UTC (rev 70758)
@@ -1 +1,3 @@
 # this is a package
+
+

Modified: grok/trunk/src/grok/tests/view/dirandinlinetemplate.py
===================================================================
--- grok/trunk/src/grok/tests/view/dirandinlinetemplate.py	2006-10-17 16:43:30 UTC (rev 70757)
+++ grok/trunk/src/grok/tests/view/dirandinlinetemplate.py	2006-10-17 17:04:10 UTC (rev 70758)
@@ -7,7 +7,7 @@
     ...
   GrokError: Conflicting templates found for name 'cavepainting' in module
   <module 'grok.tests.view.dirandinlinetemplate' from '...'>,
-  both inline and in template directory 'dirandinlinetemplate'.
+  both inline and in template directory '...dirandinlinetemplate'.
 
 """
 import grok

Modified: grok/trunk/src/grok/tests/view/dirtemplatesonly.py
===================================================================
--- grok/trunk/src/grok/tests/view/dirtemplatesonly.py	2006-10-17 16:43:30 UTC (rev 70757)
+++ grok/trunk/src/grok/tests/view/dirtemplatesonly.py	2006-10-17 17:04:10 UTC (rev 70758)
@@ -4,7 +4,7 @@
   >>> grok.grok(__name__)
   Traceback (most recent call last):
     ...
-  GrokError: Unrecognized file 'invalid.txt' in template directory 'dirtemplatesonly'.
+  GrokError: Unrecognized file 'invalid.txt' in template directory '...dirtemplatesonly'.
 """
 import grok
 



More information about the Checkins mailing list