[Checkins] SVN: martian/trunk/src/martian/ Start of the development of a new directive implementation. The idea

Martijn Faassen faassen at infrae.com
Sat Jan 26 18:19:29 EST 2008


Log message for revision 83259:
  Start of the development of a new directive implementation. The idea
  is to allow the use of directive definitions in two ways:
  
  * add directives to a class. This is the original use.
  
  * get directive information *from* the class (or module, or default).
    This is now a lot of separate utility functions which I hope to unify.
  

Changed:
  A   martian/trunk/src/martian/ndir.py
  A   martian/trunk/src/martian/ndir.txt
  U   martian/trunk/src/martian/tests/test_all.py

-=-
Added: martian/trunk/src/martian/ndir.py
===================================================================
--- martian/trunk/src/martian/ndir.py	                        (rev 0)
+++ martian/trunk/src/martian/ndir.py	2008-01-26 23:19:28 UTC (rev 83259)
@@ -0,0 +1,68 @@
+import sys
+
+from martian import util
+from martian.error import GrokImportError
+
+NOT_FOUND = object()
+
+ONCE = object()
+
+class ClassScope(object):
+    description = 'class'
+    
+    def check(self, frame):
+        return (util.frame_is_class(frame) and
+                not is_fake_module(frame))
+
+CLASS = ClassScope()
+
+class ClassOrModuleScope(object):
+    description = 'class or module'
+
+    def check(self, frame):
+        return (util.frame_is_class(frame) or
+                util.frame_is_module(frame))
+
+CLASS_OR_MODULE = ClassOrModuleScope()
+
+class Directive(object):
+    def __init__(self, namespace, name, scope, times, default):
+        self.namespace = namespace
+        self.name = name
+        self.scope = scope
+        self.times = times
+        self.default = default
+
+    def __call__(self, value):
+        frame = sys._getframe(1)
+        name = self.namespaced_name()
+        if not self.scope.check(frame):
+            raise GrokImportError("%s can only be used on %s level." %
+                                  (name, self.scope.description))
+        if name in frame.f_locals:
+            raise GrokImportError("%s can only be called once per %s." %
+                                  (name, self.scope.description))
+        frame.f_locals[name] = value
+        
+    def get(self, component, module=None):
+        name = self.namespaced_name()
+        value = getattr(component, name, NOT_FOUND)
+        if value is not NOT_FOUND:
+            return value
+        if module is not None:
+            return getattr(module, name, self.default)
+        return self.default
+    
+        return getattr(component, self.namespaced_name(), self.default)
+
+    def namespaced_name(self):
+        return self.namespace + '.' + self.name
+
+
+# this here only for testing purposes, which is a bit unfortunate
+# but makes the tests a lot clearer for module-level directives
+# also unfortunate that fake_module needs to be defined directly
+# in the fake module being tested and not in the FakeModule base class;
+# the system cannot find it on the frame if it is in the base class.
+def is_fake_module(frame):
+    return frame.f_locals.has_key('fake_module')

Added: martian/trunk/src/martian/ndir.txt
===================================================================
--- martian/trunk/src/martian/ndir.txt	                        (rev 0)
+++ martian/trunk/src/martian/ndir.txt	2008-01-26 23:19:28 UTC (rev 83259)
@@ -0,0 +1,131 @@
+Directives New Style
+====================
+
+When grokking a class, the grokking procedure can be informed by
+directives, on a class, or a module. If a directive is absent, the
+system falls back to a default. Here we introduce a general way to
+define these directives, and how to use them to retrieve information
+for a class for use during the grokking procedure.
+
+Let's define a simple directive that sets a description::
+
+  >>> from martian.ndir import Directive, CLASS, ONCE
+  >>> description = Directive('martian', 'description', 
+  ...   CLASS, ONCE, u'')
+
+This directive is placed in a namespace (in this case, ``martian``);
+this is just a string and should be used to avoid conflicts between
+directives with the same name that could be defined by different
+packages.
+
+The name of the directive is ``description``. We specify that the
+directive can only be used in the scope of a class. We also specify it
+can only be used a single time. Finally we define the default in case
+the directive is absent (the empty string).
+
+Let's look at the directive in action::
+
+  >>> class Foo(object):
+  ...    description(u"This is a description")
+
+After setting it, we can use the ``get`` method on the directive to
+retrieve it from the class again::
+
+  >>> description.get(Foo)
+  u'This is a description'
+
+Let's see that directives in different namespaces indeed get stored
+differently. We'll define a similar directive in another namespace::
+
+  >>> description2 = Directive('different', 'description', 
+  ...   CLASS, ONCE, u'')
+
+  >>> class Foo(object):
+  ...    description(u"Description1")
+  ...    description2(u"Description2")
+  >>> description.get(Foo)
+  u'Description1'
+  >>> description2.get(Foo)
+  u'Description2'
+
+Let's check the defaulting behavior. If we check the value of a class
+without the directive, we expect to see the default for that directive::
+
+  >>> class Foo(object):
+  ...   pass
+  >>> description.get(Foo)
+  u''
+
+When we use the directive outside of class scope, we expect an error message::
+
+  >>> description('Description')
+  Traceback (most recent call last):
+    ...
+  GrokImportError: martian.description can only be used on class level.
+
+In particular, we cannot use it in a module::
+
+  >>> class testmodule(FakeModule):
+  ...   fake_module = True
+  ...   description("Description")
+  Traceback (most recent call last):
+    ...
+  GrokImportError: martian.description can only be used on class level.
+
+We cannot use the directive twice in the class scope. If we do so, we
+expect an error message as well::
+
+  >>> class Foo(object):
+  ...   description(u"Description1")
+  ...   description(u"Description2")
+  Traceback (most recent call last):
+    ...
+  GrokImportError: martian.description can only be called once per class.
+
+Let's now define a ``layer`` directive that can be used in class and
+module scope both::
+
+  >>> from martian.ndir import CLASS_OR_MODULE
+  >>> layer = Directive('martian', 'layer', CLASS_OR_MODULE, ONCE, None)
+
+We can use it on a class::
+
+  >>> class Foo(object):
+  ...   layer('Test')
+  >>> layer.get(Foo)
+  'Test'
+
+The defaulting to ``None`` works::
+
+  >>> class Foo(object):
+  ...   pass
+  >>> layer.get(Foo) is None
+  True
+
+We can also use it in a module::
+
+  >>> class testmodule(FakeModule):
+  ...    layer('Test2')
+  ...    class Foo(object):
+  ...       pass
+  >>> test_module = fake_import(testmodule)
+
+When we now try to access ``layer`` on ``Foo``, we expect to find the
+module-level default which we just set. We pass the module as the second
+argument to the ``get`` method to have it fall back on this::
+
+  >>> layer.get(testmodule.Foo, testmodule)
+  'Test2'
+
+Let's try again in a module where the directive is not used::
+
+  >>> class testmodule(FakeModule):
+  ...   class Foo(object):
+  ...      pass
+  >>> testmodule = fake_import(testmodule)
+
+We expect the value to be the default, ``None``::
+
+  >>> layer.get(testmodule.Foo, testmodule) is None
+  True
+

Modified: martian/trunk/src/martian/tests/test_all.py
===================================================================
--- martian/trunk/src/martian/tests/test_all.py	2008-01-26 22:20:43 UTC (rev 83258)
+++ martian/trunk/src/martian/tests/test_all.py	2008-01-26 23:19:28 UTC (rev 83259)
@@ -71,5 +71,9 @@
         doctest.DocFileSuite('directive.txt',
                              package='martian',
                              optionflags=optionflags),
+        doctest.DocFileSuite('ndir.txt',
+                             package='martian',
+                             globs=globs,
+                             optionflags=optionflags),
         ])
     return suite



More information about the Checkins mailing list