[z3-checkins] r13521 - in z3/zopejam/trunk/src: zcmledit zcmledit/tests zopejam

hathawsh at codespeak.net hathawsh at codespeak.net
Fri Jun 17 08:31:49 CEST 2005


Author: hathawsh
Date: Fri Jun 17 08:31:45 2005
New Revision: 13521

Modified:
   z3/zopejam/trunk/src/zcmledit/configfile.py
   z3/zopejam/trunk/src/zcmledit/include.py
   z3/zopejam/trunk/src/zcmledit/tests/test_configfile.py
   z3/zopejam/trunk/src/zcmledit/tests/test_include.py
   z3/zopejam/trunk/src/zopejam/main.py
Log:
Handle invalid config files by setting an 'error' attribute rather 
than raise an exception.

This makes it possible for UI widgets to use parsed configuration 
elements as their model.  Alternatively, if configuration parse errors 
were propagated, it would be difficult to display errors in the UI 
without wrapping all configuration elements with something 
that can hold an error message.


Modified: z3/zopejam/trunk/src/zcmledit/configfile.py
==============================================================================
--- z3/zopejam/trunk/src/zcmledit/configfile.py	(original)
+++ z3/zopejam/trunk/src/zcmledit/configfile.py	Fri Jun 17 08:31:45 2005
@@ -29,28 +29,49 @@
         # package_name is None or the absolute package name that was
         # used for including this config file
         self.package_name = package_name
-        self.root = None    # A DirectiveElement
-        self.output = None   # [line, DirectiveElement, line, ...]
 
-    def load(self):
+    def load(self, pythonpath):
         """Parse the file into a tree of DirectiveElements.
 
-        Stores the result in self.root.
+        Stores the result in self.root.  If the file can't be read or
+        contains non-well-formed XML, self.error is set and self.root
+        is None.
         """
+        self.root = None     # A DirectiveElement
+        self.output = []     # [line, DirectiveElement, line, ...]
+        self.resolvers = []  # [IncludeResolver]
+        self.metadirs = []   # [DirectiveElement]
+        self.error = None
+
         fn = self.filename
         try:
             f = open(fn, 'rb')
-        except IOError, (code, msg):
+        except IOError, e:
+            code, msg = e
             fn = self.filename + ".in"
             if code != errno.ENOENT or not os.path.exists(fn):
-                raise
-            f = open(fn, 'rb')
+                self.error = "Can't read %s: %s" % (self.filename, str(e))
+                return
+            try:
+                f = open(fn, 'rb')
+            except IOError, e:
+                self.error = "Can't read %s: %s" % (fn, str(e))
+                return
 
         try:
             h = ConfigurationFileHandler(self)
-            self.root, self.output = h.parse(f)
+            try:
+                self.root, self.output = h.parse(f)
+            except expat.ExpatError, e:
+                self.error = 'XML parse error: %s' % str(e)
+                return
             if self.package_name:
                 self.root._setDefaultPackage(self.package_name)
+            for e in self.root.flattened():
+                if e.isIncludeDirective():
+                    self.resolvers.append(IncludeResolver(e, pythonpath))
+                elif e.isMetaDirective():
+                    self.metadirs.append(e)
         finally:
             f.close()
 
@@ -147,9 +168,9 @@
         """Returns True if this is an include directive"""
         return self._is_include
 
-    def getIncludeResolver(self, pythonpath):
-        assert self._is_include, "not an include directive"
-        return IncludeResolver(self, pythonpath)
+    def isMetaDirective(self):
+        """Returns True if this directive defines a directive type"""
+        return False
 
     def getXMLName(self, ns, localname):
         """Returns the tag or attribute for a given namespace and localname"""

Modified: z3/zopejam/trunk/src/zcmledit/include.py
==============================================================================
--- z3/zopejam/trunk/src/zcmledit/include.py	(original)
+++ z3/zopejam/trunk/src/zcmledit/include.py	Fri Jun 17 08:31:45 2005
@@ -15,55 +15,88 @@
 class IncludeResolver:
     """Resolves an include directive to a set of files.
 
-    Does not depend on the configured Python code to find packages.
+    Does not load the configured Python code to find packages.
     """
 
     def __init__(self, element, pythonpath):
         self.element = element
+        self.resolve(pythonpath)
+
+    def resolve(self, pythonpath):
+        """Read the directive element and prepare the filename pattern.
+
+        The constructor calls this method, but it can be called again
+        when the directive element changes, a Python package is added,
+        or the pythonpath changes.
+        """
+        element = self.element
         ns, localname = element.name
         self.override = (localname == 'includeOverrides')
         self.files = element.data.get('files')
         self.file = element.data.get('file')
         self.package = element.data.get('package')
+        self.glob = bool(self.files)
+        self.error = None
+
+        # If there is bad data in the directive, set self.error rather
+        # than raise an exception so that the error can be presented
+        # in a helpful way at a later time.
 
         if self.files and self.file:
-            raise ValueError("Must specify only one of file or files")
+            self.error = "Must specify only one of file or files"
         if not self.files and not self.file:
             self.file = 'configure.zcml'
 
-        # figure out the absolute package name, which may be None.
-        if not self.package:
-            try:
-                abs_package = element.getAbsolutePackageName()
-            except NoPackageError:
-                # Not in a package, but that's okay because the
-                # directive didn't ask for a package. The directive is
-                # referring to a file relative to the file containing
-                # the include directive.
-                abs_package = None
-        elif self.package.startswith('.'):
-            # Turn the relative package name into an absolute package
-            # name.  In this case, the directive did specify a
-            # package, so complain if there is no absolute package
-            # name in this context.
-            parent = element.getAbsolutePackageName()
-            abs_package = join_package(parent, self.package)
-        else:
-            abs_package = self.package
+        # Determine the absolute package name, which may be None.
+        try:
+            if not self.package:
+                try:
+                    abs_package = element.getAbsolutePackageName()
+                except NoPackageError:
+                    # Not in a package, but that's okay because the
+                    # directive didn't ask for a package. The directive is
+                    # referring to a file relative to the file containing
+                    # the include directive.
+                    abs_package = None
+            elif self.package.startswith('.'):
+                # Turn the relative package name into an absolute package
+                # name.  In this case, the directive did specify a
+                # package, so complain if there is no absolute package
+                # name in this context.
+                parent = element.getAbsolutePackageName()
+                abs_package = join_package(parent, self.package)
+            else:
+                abs_package = self.package
+        except PackageSpecError, e:
+            abs_package = None
+            self.error = '%s: %s' % (e.__class__.__name__, str(e))
+        self.abs_package = abs_package
 
-        fn = os.path.normpath(self.files or self.file)
-        if not os.path.isabs(fn):
+        # Determine the filename pattern
+        pattern = os.path.normpath(self.files or self.file)
+        if not os.path.isabs(pattern):
             # convert to an absolute path
             if abs_package:
-                basepath = find_package(abs_package, pythonpath)
+                try:
+                    basepath = find_package(abs_package, pythonpath)
+                except PackageSpecError, e:
+                    basepath = None
+                    self.error = '%s: %s' % (e.__class__.__name__, str(e))
             else:
                 cfn = element.config_file.filename
                 basepath = os.path.abspath(os.path.dirname(cfn))
-            fn = os.path.join(basepath, fn)
+            if basepath is not None:
+                pattern = os.path.join(basepath, pattern)
+            else:
+                pattern = None
 
-        self.abs_package = abs_package
-        self.filename = fn
-        self.glob = bool(self.files)
+        # set self.filenames, the list of files to include
+        if self.error:
+            self.filenames = []
+        elif self.glob:
+            self.filenames = glob(pattern)
+        else:
+            self.filenames = [pattern]
 
     def getAbsolutePackageName(self):
         """Returns the package name that included files belong to.
@@ -75,12 +108,9 @@
     def listFiles(self):
         """Returns the list of files to include.
 
-        May return filenames that do not exist.
+        May return names of files that do not exist.
         """
-        if self.glob:
-            return glob(self.filename)
-        else:
-            return [self.filename]
+        return self.filenames
 
 
 def join_package(parent, child):

Modified: z3/zopejam/trunk/src/zcmledit/tests/test_configfile.py
==============================================================================
--- z3/zopejam/trunk/src/zcmledit/tests/test_configfile.py	(original)
+++ z3/zopejam/trunk/src/zcmledit/tests/test_configfile.py	Fri Jun 17 08:31:45 2005
@@ -15,11 +15,12 @@
 def load_root():
     fn = os.path.join(tests_dir, 'root.zcml')
     cf = ConfigurationFile(fn)
-    cf.load()
+    cf.load(sys.path)
     return cf
 
 def test_load_root():
     cf = load_root()
+    assert not cf.error
     dirs = cf.root.flattened()
     assert len(dirs) == 4
 
@@ -34,7 +35,7 @@
 
 def try_one_preservation(fn):
     cf = ConfigurationFile(fn)
-    cf.load()
+    cf.load(sys.path)
     f = open(fn, 'rb')
     file_contents = [line.rstrip() for line in f.readlines()]
     f.close()

Modified: z3/zopejam/trunk/src/zcmledit/tests/test_include.py
==============================================================================
--- z3/zopejam/trunk/src/zcmledit/tests/test_include.py	(original)
+++ z3/zopejam/trunk/src/zcmledit/tests/test_include.py	Fri Jun 17 08:31:45 2005
@@ -59,14 +59,12 @@
     """Recursively read configuration files with include directives"""
     fn_list.append(fn)
     cf = ConfigurationFile(fn, package_name)
-    cf.load()
-    for e in cf.root.flattened():
-        if e.isIncludeDirective():
-            resolver = e.getIncludeResolver(sys.path)
-            subpackage = resolver.getAbsolutePackageName()
-            for include_fn in resolver.listFiles():
-                assert include_fn not in fn_list
-                load_and_resolve(include_fn, subpackage, fn_list)
+    cf.load(sys.path)
+    for resolver in cf.resolvers:
+        subpackage = resolver.getAbsolutePackageName()
+        for include_fn in resolver.listFiles():
+            assert include_fn not in fn_list
+            load_and_resolve(include_fn, subpackage, fn_list)
 
 
 def test_resolve_includes():

Modified: z3/zopejam/trunk/src/zopejam/main.py
==============================================================================
--- z3/zopejam/trunk/src/zopejam/main.py	(original)
+++ z3/zopejam/trunk/src/zopejam/main.py	Fri Jun 17 08:31:45 2005
@@ -23,13 +23,40 @@
 
 class Project:
 
-    def __init__(self, root_zcml, pythonpath=None):
-        self.root_zcml = root_zcml  # '/path/to/site.zcml'
-        self.base = os.path.dirname(root_zcml)
+    def __init__(self, root_filename, pythonpath=None):
+        self.root_filename = root_filename  # '/path/to/site.zcml'
+        self.base = os.path.dirname(root_filename)
         if pythonpath is None:
             pythonpath = [os.path.join(self.base, 'src')]
         self.pythonpath = pythonpath
 
+    def load(self):
+        self.packages = {}  # {package name: [filename]}
+        self.files = {}     # {filename: config_file}
+        self.root_config = ConfigurationFile(self.root_filename)
+        self.files[self.root_filename] = self.root_config
+
+        todo = [self.root_config]
+        while todo:
+            cfg = todo.pop(0)
+            cfg.load(self.pythonpath)
+
+            # add to self.packages
+            filenames = self.packages.get(cfg.package_name)
+            if filenames is None:
+                filenames = []
+                self.packages[cfg.package_name] = filenames
+            filenames.append(cfg.filename)
+
+            # add to self.files
+            for resolver in cfg.resolvers:
+                for fn in resolver.listFiles():
+                    if not self.files.has_key(fn):
+                        subcfg = ConfigurationFile(
+                            fn, resolver.getAbsolutePackageName())
+                        self.files[fn] = subcfg
+                        todo.append(subcfg)
+
 
 class PaletteImageList(wx.ImageList):
 
@@ -167,6 +194,7 @@
         if dlg.ShowModal() == wx.ID_OK:
             fn = os.path.abspath(root_zcml.GetValue())
             self.project = Project(fn)
+            self.project.load()
             self.populate()
             self.update_commands_enabled()
 
@@ -215,47 +243,36 @@
             return
 
         images = self.small_palette_images
-        packages = {}  # {package: [(filename, cfg)]}
         incroot = inctree.AddRoot('')
-        root_config = ConfigurationFile(self.project.root_zcml)
+        todo = [(incroot, self.project.root_config)]
 
-        todo = [(incroot, root_config)]
         while todo:
             parent, cfg = todo.pop(0)
 
-            filenames = packages.get(cfg.package_name or '')
-            if filenames is None:
-                filenames = []
-                packages[cfg.package_name or ''] = filenames
-            filenames.append((cfg.filename, cfg))
-            
-            cfg.load()
             label = os.path.basename(cfg.filename)
             data = wx.TreeItemData(cfg)
             image = images.get('zcmlfile', -1)
             child = inctree.AppendItem(parent, label, image=image, data=data)
-            for dir in cfg.root.flattened():
-                if dir.isIncludeDirective():
-                    resolver = dir.getIncludeResolver(self.project.pythonpath)
-                    data = wx.TreeItemData(resolver)
-                    f = resolver.files or resolver.file
-                    if resolver.abs_package:
-                        label = '[%s] %s' % (resolver.abs_package, f)
-                    else:
-                        label = f
-
-                    image_name = '%s_%s' % (
-                        resolver.override and 'includeoverride' or 'include',
-                        resolver.glob and 'glob' or 'one'
-                        )
-                    image = images.get(image_name, -1)
-
-                    gchild = inctree.AppendItem(
-                        child, label, image=image, data=data)
-                    for fn in resolver.listFiles():
-                        cfg = ConfigurationFile(
-                            fn, resolver.getAbsolutePackageName())
-                        todo.append((gchild, cfg))
+
+            for resolver in cfg.resolvers:
+                data = wx.TreeItemData(resolver)
+                f = resolver.files or resolver.file
+                if resolver.abs_package:
+                    label = '[%s] %s' % (resolver.abs_package, f)
+                else:
+                    label = f
+
+                image_name = '%s_%s' % (
+                    resolver.override and 'includeoverride' or 'include',
+                    resolver.glob and 'glob' or 'one'
+                    )
+                image = images.get(image_name, -1)
+
+                gchild = inctree.AppendItem(
+                    child, label, image=image, data=data)
+                for fn in resolver.listFiles():
+                    cfg = self.project.files[fn]
+                    todo.append((gchild, cfg))
 
         # expand the visible roots
         child, cookie = inctree.GetFirstChild(incroot)
@@ -265,15 +282,15 @@
 
         # populate pkgtree
         pkgroot = pkgtree.AddRoot('')
-        items = packages.items()
+        items = self.project.packages.items()
         items.sort()
         file_image = images.get('zcmlfile', -1)
         pkg_image = images.get('package', -1)
         for package, filenames in items:
             pkgid = pkgtree.AppendItem(
-                pkgroot, package or '[no package]', image=pkg_image)
+                pkgroot, package or '<base directory>', image=pkg_image)
             filenames.sort()
-            for filename, cfg in filenames:
+            for filename in filenames:
                 if package:
                     label = os.path.basename(filename)
                 else:


More information about the z3-checkins mailing list