[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