[Zope-CVS] SVN: zpkgtools/trunk/ Allow projects to avoid separate
configuration and resource map files:
Fred L. Drake, Jr.
fdrake at gmail.com
Sat Aug 6 00:53:36 EDT 2005
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:
More information about the Zope-CVS
mailing list