[Checkins] SVN: z3c.recipe.filetemplate/branches/gary-support-system-python/ Support the new zc.buildout 1.4.0+ include-site-packages option, and more.

Gary Poster gary.poster at canonical.com
Thu Jul 9 15:20:56 EDT 2009


Log message for revision 101775:
  Support the new zc.buildout 1.4.0+ include-site-packages option, and more.
  
  --------
  Features
  --------
  
  - Support the new zc.buildout 1.4.0+ include-site-packages option.
  
  - Use the new zc.buildout 1.4.0+ path sorting algorithm, which puts
    site-package dependency paths after the other dependency paths (but before
    extra paths, as before).  This can reduce or eliminate problems with
    packages in site-packages causing other dependencies to be masked with the
    versions in site-packages.
  
  - Support escaping "$" with "$$" in templates.  This is particularly useful
    for *NIX shell scripts.
  
  - Support specifying local options in templates without braces (e.g.,
    "Hello $world" is now equivalent to "Hello ${world}".
  
  -----
  Fixes
  -----
  
  - Clarify that the recipe does not support the relative-paths options.
  
  - Make tests less susceptible to timing errors.
  

Changed:
  _U  z3c.recipe.filetemplate/branches/gary-support-system-python/
  U   z3c.recipe.filetemplate/branches/gary-support-system-python/CHANGES.txt
  U   z3c.recipe.filetemplate/branches/gary-support-system-python/buildout.cfg
  U   z3c.recipe.filetemplate/branches/gary-support-system-python/setup.py
  U   z3c.recipe.filetemplate/branches/gary-support-system-python/z3c/recipe/filetemplate/README.txt
  U   z3c.recipe.filetemplate/branches/gary-support-system-python/z3c/recipe/filetemplate/__init__.py
  U   z3c.recipe.filetemplate/branches/gary-support-system-python/z3c/recipe/filetemplate/tests.py

-=-

Property changes on: z3c.recipe.filetemplate/branches/gary-support-system-python
___________________________________________________________________
Modified: svn:externals
   - bootstrap svn://svn.zope.org/repos/main/zc.buildout/trunk/bootstrap

   + bootstrap svn://svn.zope.org/repos/main/zc.buildout/trunk/bootstrap
zc.buildout svn+ssh://svn.zope.org/repos/main/zc.buildout/branches/gary-support-system-python


Modified: z3c.recipe.filetemplate/branches/gary-support-system-python/CHANGES.txt
===================================================================
--- z3c.recipe.filetemplate/branches/gary-support-system-python/CHANGES.txt	2009-07-09 19:18:14 UTC (rev 101774)
+++ z3c.recipe.filetemplate/branches/gary-support-system-python/CHANGES.txt	2009-07-09 19:20:56 UTC (rev 101775)
@@ -9,9 +9,29 @@
 Features
 --------
 
-- None yet.
+- Support the new zc.buildout 1.4.0+ include-site-packages option.
 
+- Use the new zc.buildout 1.4.0+ path sorting algorithm, which puts
+  site-package dependency paths after the other dependency paths (but before
+  extra paths, as before).  This can reduce or eliminate problems with
+  packages in site-packages causing other dependencies to be masked with the
+  versions in site-packages.
 
+- Support escaping "$" with "$$" in templates.  This is particularly useful
+  for *NIX shell scripts.
+
+- Support specifying local options in templates without braces (e.g.,
+  "Hello $world" is now equivalent to "Hello ${world}".
+
+-----
+Fixes
+-----
+
+- Clarify that the recipe does not support the relative-paths options.
+
+- Make tests less susceptible to timing errors.
+
+
 2.0.3 (2009-07-02)
 ==================
 

Modified: z3c.recipe.filetemplate/branches/gary-support-system-python/buildout.cfg
===================================================================
--- z3c.recipe.filetemplate/branches/gary-support-system-python/buildout.cfg	2009-07-09 19:18:14 UTC (rev 101774)
+++ z3c.recipe.filetemplate/branches/gary-support-system-python/buildout.cfg	2009-07-09 19:20:56 UTC (rev 101775)
@@ -1,7 +1,17 @@
 [buildout]
 develop = .
+          zc.buildout
+          zc.buildout/zc.recipe.egg_
 parts = test
+        interpreter
+include-site-packages = false
 
 [test]
 recipe = zc.recipe.testrunner
 eggs = z3c.recipe.filetemplate
+
+[interpreter]
+recipe = zc.recipe.egg
+interpreter = py
+eggs = z3c.recipe.filetemplate
+

Modified: z3c.recipe.filetemplate/branches/gary-support-system-python/setup.py
===================================================================
--- z3c.recipe.filetemplate/branches/gary-support-system-python/setup.py	2009-07-09 19:18:14 UTC (rev 101774)
+++ z3c.recipe.filetemplate/branches/gary-support-system-python/setup.py	2009-07-09 19:20:56 UTC (rev 101775)
@@ -42,8 +42,8 @@
       packages=find_packages(),
       namespace_packages=['z3c', 'z3c.recipe'],
       install_requires=['setuptools',
-                        'zc.buildout',
-                        'zc.recipe.egg',
+                        'zc.buildout>=1.4.0dev',
+                        'zc.recipe.egg>=1.3.0dev',
                         ],
       zip_safe=True,
       entry_points="""

Modified: z3c.recipe.filetemplate/branches/gary-support-system-python/z3c/recipe/filetemplate/README.txt
===================================================================
--- z3c.recipe.filetemplate/branches/gary-support-system-python/z3c/recipe/filetemplate/README.txt	2009-07-09 19:18:14 UTC (rev 101774)
+++ z3c.recipe.filetemplate/branches/gary-support-system-python/z3c/recipe/filetemplate/README.txt	2009-07-09 19:20:56 UTC (rev 101775)
@@ -40,7 +40,7 @@
     ... world = Philipp
     ... """)
 
-After executing buildout, we can see that ``$world`` has indeed been
+After executing buildout, we can see that ``${world}`` has indeed been
 replaced by ``Philipp``:
 
     >>> print system(buildout)
@@ -49,6 +49,37 @@
     >>> cat(sample_buildout, 'helloworld.txt')
     Hello Philipp!
 
+If the option name does not have a space or a period in it, you do not need the
+braces in the template.  Notice this example uses "$world" not "${world}" for
+the same result as before.
+
+    >>> write_and_wait(sample_buildout, 'helloworld.txt.in',
+    ... """
+    ... Hi $world!
+    ... """)
+
+    >>> print system(buildout)
+    Uninstalling message.
+    Installing message.
+
+    >>> cat(sample_buildout, 'helloworld.txt')
+    Hi Philipp!
+
+You can escape the dollar sign by repeating it, as with the Python string
+template class.
+
+    >>> write_and_wait(sample_buildout, 'helloworld.txt.in',
+    ... """
+    ... Hello $$$world! The double $${dollar-sign} escapes!
+    ... """)
+
+    >>> print system(buildout)
+    Uninstalling message.
+    Installing message.
+
+    >>> cat(sample_buildout, 'helloworld.txt')
+    Hello $Philipp! The double ${dollar-sign} escapes!
+
 Note that the output file uses the same permission bits as found on the input
 file.
 
@@ -327,14 +358,21 @@
     >>> cat(sample_buildout, 'helloworld.txt') # doctest:+ELLIPSIS
     Hello!  Here are the paths for the demo<0.3 eggs.
     OS paths:
-    .../eggs/demo-0.2...egg:.../eggs/demoneeded-1.2c1...egg
+    .../eggs/demo-0.2...egg:.../eggs/demoneeded-1.2c1...egg:...
     ---
     String paths:
-    '.../eggs/demo-0.2...egg', '.../eggs/demoneeded-1.2c1...egg'
+    '.../eggs/demo-0.2...egg', '.../eggs/demoneeded-1.2c1...egg', '...'
     ---
     Space paths:
-    .../eggs/demo-0.2...egg .../eggs/demoneeded-1.2c1...egg
+    .../eggs/demo-0.2...egg .../eggs/demoneeded-1.2c1...egg ...
 
+Notice that included multiple paths.  In fact, it includes the site packages
+and the standard library, so these are appropriate for entirely replacing
+sys.path.
+
+You can eliminate the site packages from the paths by specifying
+"include-site-packages = false" in the buildout or the specific section.
+
 You can specify extra-paths as well, which will go at the end of the egg paths.
 
     >>> write(sample_buildout, 'buildout.cfg',
@@ -359,13 +397,13 @@
     >>> cat(sample_buildout, 'helloworld.txt') # doctest:+ELLIPSIS
     Hello!  Here are the paths for the demo<0.3 eggs.
     OS paths:
-    ...demo...:...demoneeded...:.../sample-buildout/foo
+    ...demo...:...demoneeded...:.../sample-buildout/foo:...
     ---
     String paths:
-    '...demo...', '...demoneeded...', '.../sample-buildout/foo'
+    '...demo...', '...demoneeded...', '.../sample-buildout/foo', '...'
     ---
     Space paths:
-    ...demo... ...demoneeded... .../sample-buildout/foo
+    ...demo... ...demoneeded... .../sample-buildout/foo ...
 
 Defining options in Python
 ==========================
@@ -420,8 +458,8 @@
 
     >>> cat(sample_buildout, 'helloworld.txt') # doctest:+ELLIPSIS
     hello world!
-    duplicate-os-paths: ...demo-0.2...egg:...demoneeded-1.2c1...egg
-    foo-paths: ...demo-0.2...eggFOO...demoneeded-1.2c1...egg
+    duplicate-os-paths: ...demo-0.2...egg:...demoneeded-1.2c1...egg:...
+    foo-paths: ...demo-0.2...eggFOO...demoneeded-1.2c1...eggFOO...
     silly-range: [0, 1, 2, 3, 4]
     first-interpreted-option: duplicate-os-paths=(os.pathsep).join(paths)
     message-reversed-is-egassem: egassem

Modified: z3c.recipe.filetemplate/branches/gary-support-system-python/z3c/recipe/filetemplate/__init__.py
===================================================================
--- z3c.recipe.filetemplate/branches/gary-support-system-python/z3c/recipe/filetemplate/__init__.py	2009-07-09 19:18:14 UTC (rev 101774)
+++ z3c.recipe.filetemplate/branches/gary-support-system-python/z3c/recipe/filetemplate/__init__.py	2009-07-09 19:20:56 UTC (rev 101775)
@@ -22,6 +22,7 @@
 import traceback
 import zc.recipe.egg
 import zc.buildout
+import zc.buildout.buildout
 import zc.buildout.easy_install
 
 ABS_PATH_ERROR = ('%s is an absolute path. Paths must be '
@@ -44,15 +45,21 @@
             self.options.setdefault(key, value)
         # set up paths for eggs, if given
         if 'eggs' in self.options:
+            relative_paths = self.options.get(
+                'relative-paths', 
+                buildout['buildout'].get('relative-paths', 'false')
+                )
+            if relative_paths != 'false':
+                self._user_error(
+                    'This recipe does not support relative-paths.')
+                # Why? Because the relative path tricks rely on Python
+                # at runtime, and we're offering path values for arbitrary
+                # files (for instance, including bash files).
             self.eggs = zc.recipe.egg.Scripts(buildout, name, options)
             orig_distributions, ws = self.eggs.working_set()
-            # we want ws, eggs.extra_paths, eggs._relative_paths
-            all_paths = [
-                zc.buildout.easy_install.realpath(dist.location)
-                for dist in ws]
-            all_paths.extend(
-                zc.buildout.easy_install.realpath(path)
-                for path in self.eggs.extra_paths)
+            all_paths = zc.buildout.easy_install.get_path(
+                ws, self.options['executable'], self.eggs.extra_paths,
+                self.eggs.include_site_packages)
         else:
             all_paths = []
         paths = [path for path in all_paths if not path.endswith('.zip')]
@@ -195,17 +202,17 @@
                 'Destinations already exist: %s. Please make sure that '
                 'you really want to generate these automatically.  Then '
                 'move them away.', ', '.join(already_exists))
+        seen = [] # we throw this away right now, but could move this up
+        # to __init__ if valuable.
         for rel_path, last_mod, st_mode in self.actions:
             source = os.path.join(self.source_dir, rel_path)
             dest = os.path.join(self.destination_dir, rel_path[:-3])
             mode=stat.S_IMODE(st_mode)
             template=open(source).read()
-            template=re.sub(r"\$\{([^:]+?)\}", r"${%s:\1}" % self.name,
-                            template)
-            self._create_paths(os.path.dirname(dest))
             # we process the file first so that it won't be created if there
             # is a problem.
-            processed = self.options._sub(template, [])
+            processed = Template(template).substitute(self, seen)
+            self._create_paths(os.path.dirname(dest))
             result=open(dest, "wt")
             result.write(processed)
             result.close()
@@ -221,3 +228,70 @@
 
     def update(self):
         pass
+
+
+class Template:
+    # hacked from string.Template
+    pattern = re.compile(r"""
+    \$(?:
+      (?P<escaped>\$) |                   # Escape sequence of two delimiters.
+      (?P<named>[-a-z0-9_]+) |           # Delimiter and a local option without
+                                         # space or period.
+      {(?P<braced_single>[-a-z0-9 ._]+)} |
+                                         # Delimiter and a braced local option
+      {(?P<braced_double>[-a-z0-9 ._]+:[-a-z0-9 ._]+)} |
+                                         # Delimiter and a braced fully
+                                         # qualified option (that is, with
+                                         # explicit section).
+      (?P<invalid>)                      # Other ill-formed delimiter exprs.
+    )
+    """, re.IGNORECASE | re.VERBOSE)
+
+    def __init__(self, template):
+        self.template = template
+
+    # Search for $$, $identifier, ${identifier}, and any bare $'s
+
+    def _invalid(self, mo):
+        i = mo.start('invalid')
+        lines = self.template[:i].splitlines(True)
+        if not lines:
+            colno = 1
+            lineno = 1
+        else:
+            colno = i - len(''.join(lines[:-1]))
+            lineno = len(lines)
+        raise ValueError('Invalid placeholder %r in string: line %d, col %d' %
+                         (mo.group('invalid'), lineno, colno))
+
+    def _get(self, options, section, option, seen):
+        value = options.get(option, None, seen)
+        if value is None:
+            raise zc.buildout.buildout.MissingOption(
+                "Referenced option does not exist:", section, option)
+        return value
+
+    def substitute(self, recipe, seen):
+        # Helper function for .sub()
+        def convert(mo):
+            # Check the most common path first.
+            local = mo.group('named') or mo.group('braced_single')
+            if local is not None:
+                val = self._get(recipe.options, recipe.name, local, seen)
+                # We use this idiom instead of str() because the latter will
+                # fail if val is a Unicode containing non-ASCII characters.
+                return '%s' % (val,)
+            double = mo.group('braced_double')
+            if double is not None:
+                section, option = double.split(':')
+                val = self._get(
+                    recipe.buildout[section], section, option, seen)
+                return '%s' % (val,)
+            if mo.group('escaped') is not None:
+                return '$'
+            if mo.group('invalid') is not None:
+                self._invalid(mo)
+            raise ValueError('Unrecognized named group in pattern',
+                             self.pattern) # programmer error, AFAICT
+        return self.pattern.sub(convert, self.template)
+    

Modified: z3c.recipe.filetemplate/branches/gary-support-system-python/z3c/recipe/filetemplate/tests.py
===================================================================
--- z3c.recipe.filetemplate/branches/gary-support-system-python/z3c/recipe/filetemplate/tests.py	2009-07-09 19:18:14 UTC (rev 101774)
+++ z3c.recipe.filetemplate/branches/gary-support-system-python/z3c/recipe/filetemplate/tests.py	2009-07-09 19:20:56 UTC (rev 101775)
@@ -12,12 +12,24 @@
 #
 ##############################################################################
 
+import os
 import zc.buildout.testing
 import zc.buildout.tests
 from zope.testing import doctest
 
+def write_and_wait(dir, *args):
+    path = os.path.join(dir, *(args[:-1]))
+    original = os.stat(path).st_mtime
+    while os.stat(path).st_mtime == original:
+        f = open(path, 'w')
+        f.write(args[-1])
+        f.flush()
+        os.fsync(f.fileno())
+        f.close()
+
 def setUp(test):
     zc.buildout.tests.easy_install_SetUp(test)
+    test.globs['write_and_wait'] = write_and_wait
     zc.buildout.testing.install_develop('z3c.recipe.filetemplate', test)
 
 def test_suite():



More information about the Checkins mailing list