SVN: zpkgtools/trunk/ Allow projects to avoid separate configuration and resource map files:

"Fred L. Drake, Jr." <[email protected]>
Newsgroups gmane.comp.web.zope.public-cvs
Message-ID <[email protected]>
Log message for revision 37756:
  Allow projects to avoid separate configuration and resource map files:
  
  - refactor the resource map loading code so that the interpretation
    logic is handled by a helper class with a simple API
  
  - add a more specialized schema to the configuration loader which
    supports a <resources> section, allowing resource maps to be
    embedded within a zpkg configuration file
  

Changed:
  U   zpkgtools/trunk/doc/zpkg.txt
  U   zpkgtools/trunk/zpkgtools/config.py
  U   zpkgtools/trunk/zpkgtools/locationmap.py
  U   zpkgtools/trunk/zpkgtools/tests/test_config.py

-=-
Modified: zpkgtools/trunk/doc/zpkg.txt
===================================================================
--- zpkgtools/trunk/doc/zpkg.txt	2005-08-06 03:09:02 UTC (rev 37755)
+++ zpkgtools/trunk/doc/zpkg.txt	2005-08-06 04:53:36 UTC (rev 37756)
@@ -109,7 +109,8 @@
 
 Four keys are currently defined for the configuration file:
 ``build-application``, ``collect-dependencies``,
-``include-support-code``, and ``resource-map``.
+``include-support-code``, and ``resource-map``.  There is also one
+section type that can be used as well, ``<resources>``.
 
 If ``build-application`` is set to ``true``, then an application
 distribution is constructed instead of a conventional distutils
@@ -140,13 +141,25 @@
 contains::
 
   resource-map  local.map
-  resource-map  cvs://cvs.zope.org/cvs-repository:Packages2/packages.map
 
+  <resources>
+    somepkg  some/relative/path
+  </resources>
+
+  resource-map cvs://cvs.zope.org/cvs-repository:Packages2/packages.map
+
+  <resources>
+    another  some/where/else
+  </resources>
+
 **zpkg** will first load the maps in the order:
 
-1. ``/home/fdrake/local.map``
-2. ``cvs://cvs.zope.org/cvs-repository:Packages2/packages.map``
+1. embedded maps, in their order of appearance
+2. ``/home/fdrake/local.map``
+3. ``cvs://cvs.zope.org/cvs-repository:Packages2/packages.map``
 
+Note that all ``<resources>`` sections will be processed before any
+external maps are loaded, regardless of ordering.
 
 .. _ZConfig: http://www.zope.org/Members/fdrake/zconfig/
 

Modified: zpkgtools/trunk/zpkgtools/config.py
===================================================================
--- zpkgtools/trunk/zpkgtools/config.py	2005-08-06 03:09:02 UTC (rev 37755)
+++ zpkgtools/trunk/zpkgtools/config.py	2005-08-06 04:53:36 UTC (rev 37756)
@@ -48,15 +48,44 @@
         raise ValueError("value cannot be empty")
     return string
 
-SCHEMA = cfgparser.Schema(
-    ({"resource-map": non_empty_string,
-      "include-support-code": boolean,
-      "collect-dependencies": boolean,
-      "build-application": boolean,
-      }, [], None),
-    )
 
+class Schema(cfgparser.Schema, object):
 
+    def __init__(self, filename, locations):
+        # We can use the base schema for the top-level definitions,
+        # except for the <resources> section.
+        super(Schema, self).__init__(
+            ({"resource-map": non_empty_string,
+              "include-support-code": boolean,
+              "collect-dependencies": boolean,
+              "build-application": boolean,
+              }, ["resources"], None))
+        self.base = urlutils.file_url(filename)
+        self.filename = filename
+        self.locations = locations
+
+    def startSection(self, parent, typename, name):
+        if typename != "resources":
+            raise cfgparser.ConfigurationError(
+                "only <resources> sections are allowed")
+        if isinstance(parent, locationmap.LocationMap):
+            raise cfgparser.ConfigurationError(
+                "<resources> sections may not be nested")
+        return locationmap.MapLoader(self.base, self.filename, self.locations)
+
+    def addValue(self, section, key, value):
+        if isinstance(section, locationmap.MapLoader):
+            section.add(key, value)
+        else:
+            super(Schema, self).addValue(section, key, value)
+
+    def finishSection(self, section):
+        if isinstance(section, locationmap.MapLoader):
+            return section.mapping
+        return super(Schema, self).finishSection(section)
+
+
+
 class Configuration:
     """Configuration settings for **zpkg**.
 
@@ -111,7 +140,7 @@
             paths found in the configuration file.
 
         """
-        p = cfgparser.Parser(f, path, SCHEMA)
+        p = cfgparser.Parser(f, path, Schema(path, self.locations))
         cf = p.load()
         base = urlutils.file_url(os.path.abspath(basedir)) + "/"
         for value in cf.resource_map:

Modified: zpkgtools/trunk/zpkgtools/locationmap.py
===================================================================
--- zpkgtools/trunk/zpkgtools/locationmap.py	2005-08-06 03:09:02 UTC (rev 37755)
+++ zpkgtools/trunk/zpkgtools/locationmap.py	2005-08-06 04:53:36 UTC (rev 37756)
@@ -33,7 +33,7 @@
 
 
 class MapLoadingError(ValueError):
-    def __init__(self, message, filename, lineno):
+    def __init__(self, message, filename, lineno=None):
         self.filename = filename
         self.lineno = lineno
         ValueError.__init__(self, message)
@@ -142,58 +142,44 @@
         return key in self._wildcards
 
 
-def load(f, base, mapping):
-    """Parse a location map from an open file.
+class MapLoader:
 
-    :param f:  The open file to read from.
+    def __init__(self, base, filename, mapping):
+        assert base
+        self.base = base
+        self.filename = filename
+        self.mapping = mapping
+        self.local_entries = {}
+        try:
+            self.cvsbase = loader.parse(base)
+        except ValueError:
+            self.cvsbase = None
 
-    :param base:  URL corresponding to the open file.
-
-    :param mapping:  Mapping to update.
-
-    This routine is not part of the API for this module; it is
-    separated out from the `fromPathOrUrl()` function to make it
-    easier to test the map parsing aspect of this module.
-
-    """
-    assert base
-    try:
-        cvsbase = loader.parse(base)
-    except ValueError:
-        cvsbase = None
-    local_entries = {}
-    lineno = 0
-    for line in f:
-        lineno += 1
-        line = line.strip()
-        if line[:1] in ("", "#"):
-            continue
-
-        parts = line.split()
-        if len(parts) != 2:
+    def add(self, resource, url):
+        urlparts = url.split()
+        if len(urlparts) != 1:
             raise MapLoadingError("malformed package specification",
-                                  getattr(f, "name", "<unknown>"), lineno)
-        resource, url = parts
+                                  self.filename)
         try:
             cvsurl = loader.parse(url)
         except ValueError:
             # conventional URL
-            if cvsbase is None:
-                url = urlparse.urljoin(base, url)
+            if self.cvsbase is None:
+                url = urlparse.urljoin(self.base, url)
         else:
             if isinstance(cvsurl, cvsloader.RepositoryUrl):
-                if cvsbase is None:
+                if self.cvsbase is None:
                     raise MapLoadingError(
                         "repository: URLs are not supported"
                         " without a cvs: or Subversion base URL",
-                        getattr(f, "name", "<unknown>"), lineno)
-                cvsurl = cvsbase.join(cvsurl)
+                        self.filename)
+                cvsurl = self.cvsbase.join(cvsurl)
                 url = get_template_url(cvsurl)
 
-        if resource in local_entries:
+        if resource in self.local_entries:
             _logger.warn(
                 "found duplicate entry for resource %r in %s at line %d",
-                resource, getattr(f, "name", "<unknown>"), lineno)
+                resource, self.filename)
         elif resource.endswith(".*"):
             # Deal with wildcarded resources;
             # need to check if it's already there
@@ -202,20 +188,54 @@
                 raise MapLoadingError("wildcard package name specified, but"
                                       " prefix is not a legal package name: %r"
                                       % wildcard,
-                                      getattr(f, "name", "<unknown>"), lineno)
-            if not mapping._have_wildcard(wildcard):
-                mapping._add_wildcard(wildcard, url)
+                                      self.filename)
+            if not self.mapping._have_wildcard(wildcard):
+                self.mapping._add_wildcard(wildcard, url)
         elif "*" in resource:
             raise MapLoadingError("invalid wildcard specification: %r"
                                   % resource,
-                                  getattr(f, "name", "<unknown>"), lineno)
-        elif resource not in mapping:
+                                  self.filename)
+        elif resource not in self.mapping:
             # We only want to add it once, so that loading several
             # mappings causes the first defining a resource to "win":
-            mapping[resource] = url
-        local_entries[resource] = resource
+            self.mapping[resource] = url
+        self.local_entries[resource] = resource
 
 
+def load(f, base, mapping):
+    """Parse a location map from an open file.
+
+    :param f:  The open file to read from.
+
+    :param base:  URL corresponding to the open file.
+
+    :param mapping:  Mapping to update.
+
+    This routine is not part of the API for this module; it is
+    separated out from the `fromPathOrUrl()` function to make it
+    easier to test the map parsing aspect of this module.
+
+    """
+    lineno = 0
+    filename = getattr(f, "name", "<unknown>")
+    maploader = MapLoader(base, filename, mapping)
+    for line in f:
+        lineno += 1
+        line = line.strip()
+        if line[:1] in ("", "#"):
+            continue
+
+        parts = line.split()
+        if len(parts) != 2:
+            raise MapLoadingError("malformed package specification",
+                                  filename, lineno)
+        try:
+            maploader.add(*parts)
+        except MapLoadingError, e:
+            e.lineno = lineno
+            raise
+
+
 def get_template_url(parsed):
     #
     # XXX We need to distinguish between the tag being unspecified at

Modified: zpkgtools/trunk/zpkgtools/tests/test_config.py
===================================================================
--- zpkgtools/trunk/zpkgtools/tests/test_config.py	2005-08-06 03:09:02 UTC (rev 37755)
+++ zpkgtools/trunk/zpkgtools/tests/test_config.py	2005-08-06 04:53:36 UTC (rev 37756)
@@ -98,6 +98,30 @@
         self.assert_(cf.collect_dependencies)
         self.assert_(cf.include_support_code)
 
+    def test_embedded_resource_map(self):
+        cf = config.Configuration()
+        fd, path = tempfile.mkstemp(".conf", text=True)
+        f = os.fdopen(fd, "w+")
+        f.write(
+            "<resources>\n"
+            "  PKG1  svn://svn.example.net/repos/pkg1/trunk\n"
+            "  pkg2  svn://svn.example.net/repos/proj/trunk/src/pkg2\n"
+            "  rel  some/dir\n"
+            "</resources>\n")
+        f.close()
+        where = os.path.dirname(path)
+        whereurl = urlutils.file_url(where)
+        try:
+            cf.loadPath(path)
+            self.assertEqual(cf.locations["PKG1"],
+                             "svn://svn.example.net/repos/pkg1/trunk")
+            self.assertEqual(cf.locations["pkg2"],
+                             "svn://svn.example.net/repos/proj/trunk/src/pkg2")
+            self.assertEqual(cf.locations["rel"],
+                             whereurl + "/some/dir")
+        finally:
+            os.unlink(path)
+
     def load_text(self, text, path=None, basedir=None):
         if path is None:
             if basedir is None:

_______________________________________________
Zope-CVS maillist  -  [email protected]
http://mail.zope.org/mailman/listinfo/zope-cvs

Zope CVS instructions: http://dev.zope.org/CVS
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.