[cig-commits] r6346 - in cs/pythia/trunk: journal/components journal/devices journal/diagnostics pyre/applications pyre/inventory pyre/inventory/odb pyre/inventory/properties pyre/parsing/locators

leif at geodynamics.org leif at geodynamics.org
Wed Mar 21 21:47:53 PDT 2007


Author: leif
Date: 2007-03-21 21:47:52 -0700 (Wed, 21 Mar 2007)
New Revision: 6346

Added:
   cs/pythia/trunk/pyre/inventory/Item.py
   cs/pythia/trunk/pyre/inventory/MetaInventory.py
   cs/pythia/trunk/pyre/parsing/locators/StackTraceLocator.py
Modified:
   cs/pythia/trunk/journal/components/DeviceFacility.py
   cs/pythia/trunk/journal/devices/ColorRenderer.py
   cs/pythia/trunk/journal/diagnostics/Diagnostic.py
   cs/pythia/trunk/pyre/applications/Application.py
   cs/pythia/trunk/pyre/applications/ComponentHarness.py
   cs/pythia/trunk/pyre/applications/Executive.py
   cs/pythia/trunk/pyre/applications/Shell.py
   cs/pythia/trunk/pyre/inventory/ConfigContext.py
   cs/pythia/trunk/pyre/inventory/Configurable.py
   cs/pythia/trunk/pyre/inventory/Facility.py
   cs/pythia/trunk/pyre/inventory/Inventory.py
   cs/pythia/trunk/pyre/inventory/Notary.py
   cs/pythia/trunk/pyre/inventory/Trait.py
   cs/pythia/trunk/pyre/inventory/__init__.py
   cs/pythia/trunk/pyre/inventory/odb/Registry.py
   cs/pythia/trunk/pyre/inventory/properties/InputFile.py
   cs/pythia/trunk/pyre/inventory/properties/OutputFile.py
   cs/pythia/trunk/pyre/parsing/locators/ChainLocator.py
   cs/pythia/trunk/pyre/parsing/locators/FileLocator.py
   cs/pythia/trunk/pyre/parsing/locators/ScriptLocator.py
   cs/pythia/trunk/pyre/parsing/locators/SimpleFileLocator.py
   cs/pythia/trunk/pyre/parsing/locators/SimpleLocator.py
   cs/pythia/trunk/pyre/parsing/locators/__init__.py
Log:
Make some long-overdue improvements to the core framework, to address
the challenges presented by PyLith-0.8 and SPECFEM.


~~~ _validate() ~~~

First and foremost: Components can now define a _validate() method
which performs complex validation, and generates error messages which
are every bit as pretty as those produced by the framework itself
(rather than just bombing with an exception).  "Complex" validation is
defined as any validation checks involving two or more properties, and
which therefore cannot by handled by a simple validator.  For example:

    def _validate(self, context):
        Component._validate(self, context)

        # convert to degrees
        from pyre.units.angle import deg
        ANGULAR_WIDTH_XI_IN_DEGREES  = self.inventory.angular_width_xi / deg

        # this MUST be 90 degrees for two chunks or more to match geometrically
        if (self.inventory.NCHUNKS > 1 and
            ANGULAR_WIDTH_XI_IN_DEGREES != 90.0):
            context.error(ValueError("'angular-width-xi' must be 90 degrees for more than one chunk"),
                          items=[self.metainventory.angular_width_xi])

Note that the error is not raised, but reported to the 'context'
argument.  This allows the application to report multiple user errors,
as a compiler would (the gold standard to which CIG-Pyre aspires) --
rather than bombing on the first one (as CACR-Pyre often does).  The
'items' keyword argument allows the framework to report the location
from which the bogus 'angular-width-xi' value orginated (as the
framework would for its own error messages).

The _validate() method fires immediately before _configure().


~~~ MetaInventory ~~~

Notice that the above code uses something called 'metainventory'.
This was invented simply to make writing _validate() easier.  However,
it has other useful applications as well:

    # dump all inventory items, and where they came from
    for item in self.metainventory:
        print item.attr, item.name, item.value, item.locator

(This was possible all along, it just wasn't so easy.)  The addition
of 'attr' to class Trait (and the metainventory items) makes it easy
to automatically transfer all inventory items to ordinary instance
variables:

    def _configure(self):
        Component._configure(self)
        for item in self.metainventory.iteritems(MyComponent.Inventory):
            setattr(self, item.attr, item.value)

(One can achieve the same effect by declaring traits as class
attributes, instead of nested inside Inventory; see r4743.  But some
people like having a nested Inventory class -- go figure.)  The class
argument to 'iteritems' is significant: if not given, all inventory
items (including those declared in base and derived components) are
enumerated.


~~~ When 'default' is 'None', revisited ~~~

Facilities are now consistent with properties, allowing components to
specify optional plug-ins:

    plugin = pyre.facility("plug-in", default=None)

As with properties, a value of None means the facility was not set:

    if self.inventory.plugin is not None:
        # a plugin was plugged in!
    else:
        # no 'plug-in' was specified

As before, it is an error to omit both the default and the factory
method:

    plugin = pyre.facility("plug-in") # error

Revisiting r6258, inputFile and outputFile now have the original CACR
semantics when 'default' is not specified; e.g.:

    import pyre.inventory as pyre

    # defaults to 'stdout'
    dump = pyre.outputFile("dump")

    # no default; must check for 'None'
    winkler = pyre.outputFile("winkler", default=None)


~~~ Unknown components, unrecognized properties ~~~

Unknown components and unrecognized properties are now accumlated into
a registry.  In the future, this might make the ComponentHarness
implementation robust and beautiful.  The immediate benefit is that
unknown component errors are more informative:

 >> myerrors.cfg:3:
 -- pyre.inventory(error)
 -- asdljfasf.asdfljka.x <- '2'
 -- unknown component 'asdljfasf.asdfljka'

Instead of listing all the unknown components on a single line, Pyre
now reports each occurrance individually, and where it came from.


~~~ misc. ~~~

Exceptions thrown during Component instantiation are now caught.
SystemExit exceptions were mistakenly being caught by class Inventory
under Python 2.4 and earlier, now they aren't.

I cleaned up Locators a little bit, esp. how they interface with
Journal.  Added StackTraceLocator.


Modified: cs/pythia/trunk/journal/components/DeviceFacility.py
===================================================================
--- cs/pythia/trunk/journal/components/DeviceFacility.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/journal/components/DeviceFacility.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -22,31 +22,27 @@
                           vault=['devices'])
 
 
-    def _getDefaultValue(self, instance):
+    def _getBuiltInDefaultValue(self, instance):
         
-        if (self.default is None) and (self.factory is None):
-            
-            import pyre.parsing.locators
-            locator = pyre.parsing.locators.default()
-            
-            import sys
-            if hasattr(sys.stdout, 'isatty') and sys.stdout.isatty():
-                from os import environ
-                term = environ.get('TERM', 'console')
-                component = instance.retrieveComponent(term, factory=self.family, vault=self.vault)
-                if component is None:
-                    from Console import Console
-                    component = Console()
-                else:
-                    component.aliases.append(self.name)
-                    locator = pyre.parsing.locators.chain(component.getLocator(), locator)
+        import pyre.parsing.locators
+        locator = pyre.parsing.locators.default()
+
+        import sys
+        if hasattr(sys.stdout, 'isatty') and sys.stdout.isatty():
+            from os import environ
+            term = environ.get('TERM', 'console')
+            component = instance.retrieveComponent(term, factory=self.family, vault=self.vault)
+            if component is None:
+                from Console import Console
+                component = Console()
             else:
-                from Stream import Stream
-                component = Stream(sys.stdout, "stdout")
+                component.aliases.append(self.name)
+                locator = pyre.parsing.locators.chain(component.getLocator(), locator)
+        else:
+            from Stream import Stream
+            component = Stream(sys.stdout, "stdout")
 
-            return component, locator
-        
-        return super(DeviceFacility, self)._getDefaultValue(instance)
+        return component, locator
 
 
 # version

Modified: cs/pythia/trunk/journal/devices/ColorRenderer.py
===================================================================
--- cs/pythia/trunk/journal/devices/ColorRenderer.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/journal/devices/ColorRenderer.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -27,10 +27,13 @@
             colorKey = k
             if colorKey == 'severity':
                 colorKey = colorKey + '-' + v
-            colorized[k] = (
-                getattr(TermColors, colorScheme[colorKey]) +
-                str(v) +
-                getattr(TermColors, colorScheme['normal']))
+            try:
+                colorized[k] = (
+                    getattr(TermColors, colorScheme[colorKey]) +
+                    str(v) +
+                    getattr(TermColors, colorScheme['normal']))
+            except AttributeError:
+                colorized[k] = v
         
         return colorized
 

Modified: cs/pythia/trunk/journal/diagnostics/Diagnostic.py
===================================================================
--- cs/pythia/trunk/journal/diagnostics/Diagnostic.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/journal/diagnostics/Diagnostic.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -13,8 +13,7 @@
 
 
 import journal
-import traceback
-import linecache
+from pyre.parsing.locators import here
 from Entry import Entry
 
 
@@ -39,31 +38,17 @@
         meta = self._entry.meta
         
         if locator is None:
-            stackDepth = -2
-            stackTrace = traceback.extract_stack()
-            meta["stack-trace"] = stackTrace[:stackDepth+1]
-            file, line, function, src = stackTrace[stackDepth]
-        else:
-            try:
-                file = locator.source
-            except AttributeError:
-                file = ""
-            try:
-                line = locator.line
-            except AttributeError:
-                line = ""
-                src = ""
-            else:
-                src = linecache.getline(locator.source, locator.line)
-                src = src.rstrip()
-            function = ""
+            locator = here(1)
 
+        # These are required by 'journal.devices.Renderer'.
+        meta["filename"] = ""
+        meta["function"] = ""
+        meta["line"] = ""
+        
+        locator.getAttributes(meta)
+
         meta["facility"] = self.facility
         meta["severity"] = self.severity
-        meta["filename"] = file
-        meta["function"] = function
-        meta["line"] = line
-        meta["src"] = src
 
         journal.journal().record(self._entry)
 
@@ -111,7 +96,7 @@
     state = property(_getState, _setState, None, "")
 
 
-    class Fatal(Exception):
+    class Fatal(SystemExit):
 
 
         def __init__(self, msg=""):

Modified: cs/pythia/trunk/pyre/applications/Application.py
===================================================================
--- cs/pythia/trunk/pyre/applications/Application.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/pyre/applications/Application.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -74,7 +74,7 @@
                 try:
                     shelf = codec.open(base)
                 except Exception, error:
-                    context.error(error, 'inventory', None, locator)
+                    context.error(error, locator=locator)
                 else:
                     paramRegistry = shelf['inventory'].getFacility(self.name)
                     if paramRegistry:

Modified: cs/pythia/trunk/pyre/applications/ComponentHarness.py
===================================================================
--- cs/pythia/trunk/pyre/applications/ComponentHarness.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/pyre/applications/ComponentHarness.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -31,7 +31,7 @@
         # collect unknown traits for the components and its subcomponents
         context = self.configureHarnessedComponent(component, curator, registry)
 
-        if not context.verifyConfiguration('strict'):
+        if not context.verifyConfiguration(component, 'strict'):
             return
 
         # initialize the component

Modified: cs/pythia/trunk/pyre/applications/Executive.py
===================================================================
--- cs/pythia/trunk/pyre/applications/Executive.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/pyre/applications/Executive.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -80,7 +80,7 @@
     def verifyConfiguration(self, context, mode='strict'):
         """verify that the user input did not contain any typos"""
 
-        return context.verifyConfiguration(mode)
+        return context.verifyConfiguration(self, mode)
 
 
     # the default application action

Modified: cs/pythia/trunk/pyre/applications/Shell.py
===================================================================
--- cs/pythia/trunk/pyre/applications/Shell.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/pyre/applications/Shell.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -86,7 +86,7 @@
         self.applyConfiguration(context)
 
         # verify that my input did not contain any typos
-        if not context.verifyConfiguration('strict'):
+        if not context.verifyConfiguration(self, 'strict'):
             import sys
             sys.exit("%s: configuration error(s)" % self.name)
 
@@ -97,6 +97,9 @@
         if self.excepthook:
             sys.excepthook = self.excepthook.excepthook
 
+        # start fresh
+        context = app.newConfigContext()
+        
         # initialize the application
         app.updateConfiguration(app.registry)
         app.applyConfiguration(context)

Modified: cs/pythia/trunk/pyre/inventory/ConfigContext.py
===================================================================
--- cs/pythia/trunk/pyre/inventory/ConfigContext.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/pyre/inventory/ConfigContext.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -13,47 +13,86 @@
 
 class ConfigContext(object):
 
+    #
+    # application interface
+    #
+    
+    def error(self, error, **attributes):
+        for k, v in attributes.iteritems():
+            setattr(error, k, v)
+        if not hasattr(error, 'locator'):
+            if hasattr(error, 'items') and len(error.items) == 1:
+                error.locator = error.items[0].locator
+            else:
+                from pyre.parsing.locators import simple
+                error.locator = simple("validator")
+        if hasattr(error, 'items'):
+            for item in error.items:
+                # This could be wrong in _validate().  For now, the
+                # framework assumes that _validate() is simple and
+                # polite: i.e., a component only reports errors for
+                # its own traits.
+                item.path = self.path + [item.name]
+        if not hasattr(error, 'channel'):
+            error.channel = 'e'
+        self.errors.append(error)
+        return
 
-    def error(self, *args):
-        self.ue.append(args)
 
+    #
+    # private
+    #
 
-    def unknownComponent(self, name):
-        self.uuc.append(name)
+    def unknownComponent(self, name, registry):
+        self.unknownComponents.attachNode(registry)
 
 
-    def unknownProperty(self, *args):
-        self.uup.append(args)
+    def unrecognizedProperty(self, name, value, locator):
+        self.unrecognizedProperties.setProperty(name, value, locator)
 
 
-    def claim(self, name):
+    def configureComponent(self, component):
 
-        if not (self.uup or self.uuc or self.ue):
-            return True
+        # push
+        parent = (self.unrecognizedProperties, self.unknownComponents)
+        self.unrecognizedProperties = self.unrecognizedProperties.getNode(component.name)
+        self.unknownComponents = self.unknownComponents.getNode(component.name)
+        self.path.append(component.name)
         
-        self.up = [ (name + '.' + key, value, locator)
-                    for key, value, locator in self.uup ]
-        
-        self.uc = [ name + '.' + key
-                    for key in self.uuc]
-        
-        self.ue = [ (error, name + '.' + key, value, locator)
-                    for error, key, value, locator in self.ue ]
-        
-        self.unknownProperties += self.uup
-        self.unknownComponents += self.uuc
-        self.errors += self.ue
-        
-        self.uup = []
-        self.uuc = []
-        self.ue = []
-        
-        return False
+        # apply user settings to the component's properties
+        component.configureProperties(self)
 
+        # apply user settings to the component's subcomponents
+        component.configureComponents(self)
 
-    def verifyConfiguration(self, modeName):
+        # give the component an opportunity to perform complex validation
+        component._validate(self)
+
+        # pop
+        self.path.pop()
+        self.unrecognizedProperties, self.unknownComponents = parent
+
+        return
+
+
+    def verifyConfiguration(self, component, modeName):
         """verify that the user input did not contain any typos"""
 
+        # Convert all unrecognized properties and unknown components
+        # into errors.
+
+        node = self.unrecognizedProperties.getNode(component.name)
+        for path, value, locator in node.allProperties():
+            self.error(ConfigContext.UnrecognizedPropertyError(path, value, locator))
+
+        node = self.unknownComponents.getNode(component.name)
+        for path, value, locator in node.allProperties():
+            self.error(ConfigContext.UnknownComponentError(path, value, locator))
+
+        # Log all configuration errors and warnings.  Determine the
+        # severity of property/component typos as a function of the
+        # given mode.
+
         class Channel(object):
             def __init__(self, factory):
                 self.channel = factory("pyre.inventory")
@@ -77,46 +116,73 @@
         
         channel = mode[modeName]
 
-        if self.unknownProperties:
-            for key, value, locator in self.unknownProperties:
-                problem = ("unrecognized property '%s'" % key, key, value, locator)
-                self.log(channel['up'], problem)
+        for e in self.errors:
+            self.log(channel[e.channel], e)
 
-        if self.unknownComponents:
-            self.log(channel['uc'], ("unknown components: %s" % ", ".join(self.unknownComponents),
-                                     None, None, None))
-        
-        if self.errors:
-            for problem in self.errors:
-                self.log(channel['e'], problem)
-
         return error.tally == 0
 
 
-    def log(self, channel, problem):
+    def log(self, channel, error):
+
+        locator = None
+
+        # Perhaps there should be a decorator which normalizes the
+        # 'error' interface...
+
+        if hasattr(error, 'locator'):
+            locator = error.locator
+
+        if hasattr(error, 'items'):
+            for item in error.items:
+                path = '.'.join(item.path[1:])
+                channel.line("%s <- '%s'" % (path, item.value))
+        elif hasattr(error, 'path'):
+            path = '.'.join(error.path[1:])
+            channel.line("%s <- '%s'" % (path, error.value))
         
-        error, key, value, locator = problem
-
-        if value:
-            channel.line("%s <- '%s'" % (key, value))
         channel.log(error, locator)
-
         channel.tally = channel.tally + 1
         
         return
 
 
     def __init__(self):
-        self.unknownProperties = []
-        self.unknownComponents = []
+        from pyre.inventory import registry
+        
+        self.unrecognizedProperties = registry("inventory")
+        self.unknownComponents = registry("inventory")
+        self.path = []
+
         self.errors = []
 
-        # unclaimed errors
-        self.uup = []
-        self.uuc = []
-        self.ue = []
-
         return
 
 
+    class ConfigurationError(Exception):
+
+        def __init__(self, path, value, locator):
+            Exception.__init__(self)
+            self.path = path
+            self.value = value
+            self.locator = locator
+
+
+    class UnrecognizedPropertyError(ConfigurationError):
+
+        channel = 'up'
+        
+        def __str__(self):
+            prop = '.'.join(self.path[1:])
+            return "unrecognized property '%s'" % prop
+
+
+    class UnknownComponentError(ConfigurationError):
+        
+        channel = 'uc'
+        
+        def __str__(self):
+            component = '.'.join(self.path[1:-1])
+            return "unknown component '%s'" % component
+        
+
 # end of file 

Modified: cs/pythia/trunk/pyre/inventory/Configurable.py
===================================================================
--- cs/pythia/trunk/pyre/inventory/Configurable.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/pyre/inventory/Configurable.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -70,13 +70,9 @@
 
         if context is None:
             context = self.newConfigContext()
-        
-        # apply user settings to my properties
-        self.configureProperties(context)
 
-        # apply user settings to my components
-        self.configureComponents(context)
-
+        context.configureComponent(self)
+        
         # give descendants a chance to adjust to configuration changes
         self._configure()
         
@@ -116,13 +112,11 @@
     def configureProperties(self, context):
         """set the values of all the properties and facilities in my inventory"""
         self.inventory.configureProperties(context)
-        self._claim(context)
 
 
     def configureComponents(self, context):
         """guide my subcomponents through the configuration process"""
         self.inventory.configureComponents(context)
-        self._claim(context)
 
 
     def getDepositories(self):
@@ -341,6 +335,10 @@
         else:
             self.name = name
         self.inventory = self.createInventory()
+        
+        # provide simple, convenient access to descriptors
+        from MetaInventory import MetaInventory
+        self.metainventory = MetaInventory(self.inventory)
 
         # other names by which I am known for configuration purposes
         self.aliases = [ name ]
@@ -365,6 +363,11 @@
         return
 
 
+    def _validate(self, context):
+        """perform complex validation involving multiple properties"""
+        return
+
+
     def _configure(self):
         """modify the configuration programmatically"""
         return
@@ -380,12 +383,6 @@
         return
 
 
-    # misc
-    def _claim(self, context):
-        """decorate the missing traits with my name"""
-        return context.claim(self.name)
-
-
     # inventory
     from Inventory import Inventory
 

Modified: cs/pythia/trunk/pyre/inventory/Facility.py
===================================================================
--- cs/pythia/trunk/pyre/inventory/Facility.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/pyre/inventory/Facility.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -13,12 +13,13 @@
 
 
 from Trait import Trait
+from pyre.inventory import Uninit
 
 
 class Facility(Trait):
 
 
-    def __init__(self, name, family=None, default=None, factory=None, args=(), meta=None,
+    def __init__(self, name, family=None, default=Uninit, factory=None, args=(), meta=None,
                  vault=None):
         Trait.__init__(self, name, 'facility', default, meta)
 
@@ -36,12 +37,19 @@
 
 
     def _getDefaultValue(self, instance):
-        component = self.default
+        
+        # Initialize my value (preventing further lookups), in case we
+        # don't make it out of here alive.
+        import pyre.parsing.locators
+        from pyre.inventory import Error
+        locator = pyre.parsing.locators.error()
+        instance._initializeTraitValue(self.name, Error, locator)
 
         import pyre.parsing.locators
         locator = pyre.parsing.locators.default()
 
-        if component is not None:
+        if not self.default in [None, Uninit]:
+            component = self.default
             # if we got a string, resolve
             if isinstance(component, basestring):
                 component, loc = self._retrieveComponent(instance, component)
@@ -51,7 +59,7 @@
 
         if self.factory is not None:
             # instantiate the component
-            component =  self.factory(*self.args)
+            component = self.factory(*self.args)
             # adjust the configuration aliases to include my name
             aliases = component.aliases
             if self.name not in aliases:
@@ -60,14 +68,25 @@
             # return
             return component, locator
 
-        # oops: expect exceptions galore!
-        import journal
-        firewall = journal.firewall('pyre.inventory')
-        firewall.log(
-            "facility %r was given neither a default value nor a factory method" % self.name)
+        component, locator = self._getBuiltInDefaultValue(instance)
+        if component is not None:
+            return component, locator
+
+        if self.default is Uninit:
+            # oops: expect exceptions galore!
+            import journal
+            firewall = journal.firewall('pyre.inventory')
+            firewall.log(
+                "facility %r was given neither a default value nor a factory method" % self.name)
+
+        # None is a special value; it means that a facility is not set
         return None, None
 
 
+    def _getBuiltInDefaultValue(self, instance):
+        return None, None
+
+
     def _set(self, instance, component, locator):
         if isinstance(component, basestring):
             component, source = self._retrieveComponent(instance, component)
@@ -153,12 +172,6 @@
 
     def _import(self, instance, name):
 
-        # Initialize my value (preventing further lookups), in case we
-        # don't make it out of here alive.
-        import pyre.parsing.locators
-        locator = pyre.parsing.locators.error()
-        instance._initializeTraitValue(self.name, None, locator)
-
         factoryName = self.family
         path = name.split(':')
         c = len(path)

Modified: cs/pythia/trunk/pyre/inventory/Inventory.py
===================================================================
--- cs/pythia/trunk/pyre/inventory/Inventory.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/pyre/inventory/Inventory.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -44,20 +44,22 @@
 
     def configureProperties(self, context):
         """configure my properties using user settings in my registry"""
-        
+
         # loop over the registry property entries and
         # attempt to set the value of the corresponding inventory item
-        for name, descriptor in self._priv_registry.properties.iteritems():
-            try:
-                prop = self._traitRegistry[name]
-            except KeyError:
-                context.unknownProperty(name, descriptor.value, descriptor.locator)
-                continue
-            
-            try:
-                prop._set(self, descriptor.value, descriptor.locator)
-            except Exception, error:
-                context.error(error, name, descriptor.value, descriptor.locator)
+        for name, descriptor in self._priv_registry.properties.items():
+            prop = self._traitRegistry.get(name, None)
+            if prop:
+                try:
+                    prop._set(self, descriptor.value, descriptor.locator)
+                except SystemExit:
+                    raise
+                except Exception, error:
+                    from Item import Item
+                    context.error(error, items=[Item(prop, descriptor)])
+            else:
+                context.unrecognizedProperty(name, descriptor.value, descriptor.locator)
+                self._priv_registry.deleteProperty(name)
 
         return
 
@@ -93,12 +95,10 @@
         # note that this only affects components for which there are settings in the registry
         # this is done in a separate loop because it provides an easy way to catch typos
         # on the command line
-        for name, registry in self._priv_registry.facilities.iteritems():
-            try:
-                component = aliases[name]
-            except KeyError:
-                context.unknownComponent(name)
-                continue
+        for name in self._priv_registry.facilities.keys():
+            if not aliases.has_key(name):
+                node = self._priv_registry.extractNode(name)
+                context.unknownComponent(name, node)
 
         return
 
@@ -317,6 +317,17 @@
         return self._forceInitialization(traitName)
 
 
+    def getTraitLocator(self, traitName):
+        try:
+            return self._getTraitLocator(traitName)
+
+        except KeyError:
+            pass
+        
+        self._forceInitialization(traitName)
+        return self._getTraitLocator(traitName)
+
+
     def getTrait(self, traitName):
         return self._traitRegistry[traitName]
 
@@ -345,22 +356,27 @@
     def components(self, context=None):
         """return a list of my components"""
 
+        from pyre.inventory import Error
+
         candidates = []
 
         for name, facility in self._facilityRegistry.iteritems():
             try:
                 component = facility.__get__(self)
-                candidates.append(component)
+                if component and component is not Error:
+                    candidates.append(component)
+            except SystemExit:
+                raise
             except Exception, error:
                 if context:
                     import sys, traceback, pyre.parsing.locators
-                    file, line, function, text = traceback.extract_tb(sys.exc_info()[2])[-1]
-                    locator = pyre.parsing.locators.script(file, line, function)
-                    context.error(error, name, None, locator)
+                    stackTrace = traceback.extract_tb(sys.exc_info()[2])
+                    locator = pyre.parsing.locators.stackTrace(stackTrace)
+                    context.error(error, locator=locator)
                 else:
                     raise
         
-        return filter(None, candidates)
+        return candidates
 
 
     def __init__(self, name):
@@ -409,6 +425,10 @@
         return
 
 
+    def _getTraitLocator(self, name):
+        return self._getTraitDescriptor(name).locator
+
+
     def _createTraitDescriptor(self):
         from Descriptor import Descriptor
         return Descriptor()
@@ -431,6 +451,7 @@
     # trait registries
     _traitRegistry = {}
     _facilityRegistry = {}
+    _myTraitRegistry = {}
 
 
     # metaclass

Added: cs/pythia/trunk/pyre/inventory/Item.py
===================================================================
--- cs/pythia/trunk/pyre/inventory/Item.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/pyre/inventory/Item.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+#                      California Institute of Technology
+#                        (C) 2007  All Rights Reserved
+#
+# {LicenseText}
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+
+
+class Item(object):
+    """A Trait paired with its value and locator."""
+    
+    def __init__(self, trait, descriptor):
+        self._trait = trait
+        self._descriptor = descriptor
+
+    attr     = property(lambda self: self._trait.attr)
+    name     = property(lambda self: self._trait.name)
+    default  = property(lambda self: self._trait.default)
+    type     = property(lambda self: self._trait.type)
+    meta     = property(lambda self: self._trait.meta)
+    value    = property(lambda self: self._descriptor.value)
+    locator  = property(lambda self: self._descriptor.locator)
+
+
+# end of file

Added: cs/pythia/trunk/pyre/inventory/MetaInventory.py
===================================================================
--- cs/pythia/trunk/pyre/inventory/MetaInventory.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/pyre/inventory/MetaInventory.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+#                      California Institute of Technology
+#                        (C) 2007  All Rights Reserved
+#
+# {LicenseText}
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+
+
+from Item import Item
+
+
+class MetaInventory(object):
+
+
+    def __init__(self, inventory):
+        self._inventory = inventory
+
+
+    def __getattr__(self, name):
+        trait = getattr(self._inventory.__class__, name)
+        descriptor = self._inventory.getTraitDescriptor(trait.name)
+        return Item(trait, descriptor)
+
+
+    def iteritems(self, cls=None):
+        if cls is None:
+            registry = self._inventory._traitRegistry
+        else:
+            if not isinstance(self._inventory, cls):
+                raise TypeError("inventory object is not an instance of '%s'" % cls)
+            registry = cls._myTraitRegistry
+        for trait in registry.itervalues():
+            descriptor = self._inventory.getTraitDescriptor(trait.name)
+            yield Item(trait, descriptor)
+        return
+
+
+    def __iter__(self):
+        return self.iteritems()
+
+
+# end of file

Modified: cs/pythia/trunk/pyre/inventory/Notary.py
===================================================================
--- cs/pythia/trunk/pyre/inventory/Notary.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/pyre/inventory/Notary.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -24,6 +24,7 @@
 
         traitRegistry = {}
         facilityRegistry = {}
+        myTraitRegistry = {}
 
         # register inherited traits
         bases = list(bases)
@@ -46,12 +47,14 @@
             if not isinstance(item, Trait):
                 continue
 
+            item.attr = name
             # set the public name of trait if it is not set already
             if item.name is None:
                 item.name = name
 
             # register it
             traitRegistry[item.name] = item
+            myTraitRegistry[item.name] = item
 
             # facilities also go into their own bucket
             if isinstance(item, Facility):
@@ -60,6 +63,7 @@
         # install the registries into the class record
         cls._traitRegistry = traitRegistry
         cls._facilityRegistry = facilityRegistry
+        cls._myTraitRegistry = myTraitRegistry
 
         return
 

Modified: cs/pythia/trunk/pyre/inventory/Trait.py
===================================================================
--- cs/pythia/trunk/pyre/inventory/Trait.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/pyre/inventory/Trait.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -16,6 +16,7 @@
 
 
     def __init__(self, name, type, default=None, meta=None):
+        self.attr = None  # private attribute name (set by Notary)
         self.name = name
         self.default = default
 

Modified: cs/pythia/trunk/pyre/inventory/__init__.py
===================================================================
--- cs/pythia/trunk/pyre/inventory/__init__.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/pyre/inventory/__init__.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -165,6 +165,14 @@
     return Not(v)
 
 
+# special values
+class ErrorType(object): pass
+Error = ErrorType()
+
+class UninitType(object): pass
+Uninit = UninitType()
+
+
 # version
 __id__ = "$Id: __init__.py,v 1.4 2005/04/14 22:25:12 pyre Exp $"
 

Modified: cs/pythia/trunk/pyre/inventory/odb/Registry.py
===================================================================
--- cs/pythia/trunk/pyre/inventory/odb/Registry.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/pyre/inventory/odb/Registry.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -90,21 +90,22 @@
 
 
     def render(self):
+        listing = []
+        for path, value, locator in self.allProperties():
+            listing.append(('.'.join(path), value, "%s" % locator))
+        return listing
 
-        listing = [
-            ("%s.%s" % (self.name, name), descriptor.value, "%s" % descriptor.locator)
-            for name, descriptor in self.properties.iteritems()
-            ]
 
-        listing += [
-            ("%s.%s" % (self.name, name), value, "%s" % locator)
-            for facility in self.facilities.itervalues()
-            for name, value, locator in facility.render() 
-            ]
+    def allProperties(self):
+        """recursively iterate my properties"""
+        for name, descriptor in self.properties.iteritems():
+            yield ((self.name, name), descriptor.value, descriptor.locator)
+        for facility in self.facilities.itervalues():
+            for path, value, locator in facility.allProperties():
+                yield ((self.name,) + path, value, locator)
+        return
 
-        return listing
 
-
     def __init__(self, name):
         self.name = name
         self.properties = {}

Modified: cs/pythia/trunk/pyre/inventory/properties/InputFile.py
===================================================================
--- cs/pythia/trunk/pyre/inventory/properties/InputFile.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/pyre/inventory/properties/InputFile.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -13,12 +13,13 @@
 
 
 from pyre.inventory.Property import Property
+import sys
 
 
 class InputFile(Property):
 
 
-    def __init__(self, name, default=None, meta=None, validator=None):
+    def __init__(self, name, default=sys.stdin, meta=None, validator=None):
         Property.__init__(self, name, "file", default, meta, validator)
         return
 

Modified: cs/pythia/trunk/pyre/inventory/properties/OutputFile.py
===================================================================
--- cs/pythia/trunk/pyre/inventory/properties/OutputFile.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/pyre/inventory/properties/OutputFile.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -13,12 +13,13 @@
 
 
 from pyre.inventory.Property import Property
+import sys
 
 
 class OutputFile(Property):
 
 
-    def __init__(self, name, default=None, meta=None, validator=None):
+    def __init__(self, name, default=sys.stdout, meta=None, validator=None):
         Property.__init__(self, name, "file", default, meta, validator)
         return
 

Modified: cs/pythia/trunk/pyre/parsing/locators/ChainLocator.py
===================================================================
--- cs/pythia/trunk/pyre/parsing/locators/ChainLocator.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/pyre/parsing/locators/ChainLocator.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -25,6 +25,10 @@
         return "%s via %s" % (self.this, self.next)
 
 
+    def getAttributes(self, attr):
+        return
+
+
     __slots__ = ("this", "next")
     
 

Modified: cs/pythia/trunk/pyre/parsing/locators/FileLocator.py
===================================================================
--- cs/pythia/trunk/pyre/parsing/locators/FileLocator.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/pyre/parsing/locators/FileLocator.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -24,6 +24,14 @@
 
     def __str__(self):
         return "{file=%r, line=%r, column=%r}" % (self.source, self.line, self.column)
+
+
+    def getAttributes(self, attr):
+        import linecache
+        attr["filename"] = self.source
+        attr["line"] = self.line
+        attr["src"] = linecache.getline(self.source, self.line).rstrip()
+        return
     
 
     __slots__ = ("source", "line", "column")

Modified: cs/pythia/trunk/pyre/parsing/locators/ScriptLocator.py
===================================================================
--- cs/pythia/trunk/pyre/parsing/locators/ScriptLocator.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/pyre/parsing/locators/ScriptLocator.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -24,7 +24,15 @@
 
     def __str__(self):
         return "{file=%r, line=%r, function=%r}" % (self.source, self.line, self.function)
+
     
+    def getAttributes(self, attr):
+        import linecache
+        attr["filename"] = self.source
+        attr["line"] = self.line
+        attr["function"] = self.function
+        attr["src"] = linecache.getline(self.source, self.line).rstrip()
+        return
 
 
     __slots__ = ("source", "line", "function")

Modified: cs/pythia/trunk/pyre/parsing/locators/SimpleFileLocator.py
===================================================================
--- cs/pythia/trunk/pyre/parsing/locators/SimpleFileLocator.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/pyre/parsing/locators/SimpleFileLocator.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -22,8 +22,13 @@
 
     def __str__(self):
         return "{file=%r}" % (self.source)
+
     
+    def getAttributes(self, attr):
+        attr["filename"] = self.source
+        return
 
+
     __slots__ = ("source")
 
 # version

Modified: cs/pythia/trunk/pyre/parsing/locators/SimpleLocator.py
===================================================================
--- cs/pythia/trunk/pyre/parsing/locators/SimpleLocator.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/pyre/parsing/locators/SimpleLocator.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -24,6 +24,11 @@
         return "{%s}" % self.source
 
 
+    def getAttributes(self, attr):
+        attr['filename'] = str(self)
+        return
+
+
     __slots__ = ("source")
     
 

Added: cs/pythia/trunk/pyre/parsing/locators/StackTraceLocator.py
===================================================================
--- cs/pythia/trunk/pyre/parsing/locators/StackTraceLocator.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/pyre/parsing/locators/StackTraceLocator.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+#                      California Institute of Technology
+#                        (C) 2007  All Rights Reserved
+#
+# {LicenseText}
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+
+
+class StackTraceLocator(object):
+
+
+    def __init__(self, stackTrace):
+        self.stackTrace = stackTrace
+        return
+
+
+    def __str__(self):
+        filename, line, function, src = self.stackTrace[-1]
+        return "{file=%r, line=%r, function=%r}" % (filename, line, function)
+
+
+    def getAttributes(self, attr):
+        filename, line, function, src = self.stackTrace[-1]
+        attr["stack-trace"] = self.stackTrace
+        attr["filename"] = filename
+        attr["function"] = function
+        attr["line"] = line
+        attr["src"] = src
+        return
+
+
+# end of file 

Modified: cs/pythia/trunk/pyre/parsing/locators/__init__.py
===================================================================
--- cs/pythia/trunk/pyre/parsing/locators/__init__.py	2007-03-22 00:07:15 UTC (rev 6345)
+++ cs/pythia/trunk/pyre/parsing/locators/__init__.py	2007-03-22 04:47:52 UTC (rev 6346)
@@ -52,6 +52,21 @@
     return ChainLocator(this, next)
 
 
+def stackTrace(st):
+    from StackTraceLocator import StackTraceLocator
+    return StackTraceLocator(st)
+
+
+def here(depth=0):
+    """return the current traceback as a locator"""
+    import traceback
+    from StackTraceLocator import StackTraceLocator
+    st = traceback.extract_stack()
+    depth += 1 # account for this function
+    st = st[:-depth]
+    return StackTraceLocator(st)
+
+
 # version
 __id__ = "$Id: __init__.py,v 1.1.1.1 2005/03/08 16:13:48 aivazis Exp $"
 



More information about the cig-commits mailing list