[Checkins] SVN: zc.buildout/trunk/ Allow custom python interpreters (other than the one used to run the

Jim Fulton cvs-admin at zope.org
Mon Jun 19 15:20:28 EDT 2006


Log message for revision 68763:
  Allow custom python interpreters (other than the one used to run the
  buildout) to be used.
  

Changed:
  U   zc.buildout/trunk/eggrecipe/src/zc/recipe/egg/README.txt
  U   zc.buildout/trunk/eggrecipe/src/zc/recipe/egg/egg.py
  A   zc.buildout/trunk/eggrecipe/src/zc/recipe/egg/selecting-python.txt
  U   zc.buildout/trunk/eggrecipe/src/zc/recipe/egg/tests.py
  U   zc.buildout/trunk/src/zc/buildout/easy_install.py
  U   zc.buildout/trunk/src/zc/buildout/easy_install.txt
  U   zc.buildout/trunk/src/zc/buildout/egglinker.py
  U   zc.buildout/trunk/src/zc/buildout/egglinker.txt
  U   zc.buildout/trunk/src/zc/buildout/testing.py
  U   zc.buildout/trunk/src/zc/buildout/tests.py

-=-
Modified: zc.buildout/trunk/eggrecipe/src/zc/recipe/egg/README.txt
===================================================================
--- zc.buildout/trunk/eggrecipe/src/zc/recipe/egg/README.txt	2006-06-19 18:02:54 UTC (rev 68762)
+++ zc.buildout/trunk/eggrecipe/src/zc/recipe/egg/README.txt	2006-06-19 19:20:24 UTC (rev 68763)
@@ -1,7 +1,7 @@
 Installation of distributions as eggs
 =====================================
 
-The zc.recipe.egg ewcipe can be used to install various types if
+The zc.recipe.egg recipe can be used to install various types if
 distutils distributions as eggs.  It takes a number of options:
 
 distribution
@@ -14,6 +14,18 @@
 find-links
    A list of URLs, files, or directories to search for distributions.
 
+python
+   The name of a section to get the Python executable from.
+   If not specified, then the buildout python option is used.  The
+   Python executable is found in the executable option of the named
+   section. 
+
+unzip
+   The value of this option must be either true or false. If the value
+   is true, then the installed egg will be unzipped. Note that this is
+   only effective when an egg is installed.  If a zipped egg already 
+   exists in the eggs directory, it will not be unzipped.
+
 To illustrate this, we've created a directory with some sample eggs:
 
     >>> ls(sample_eggs)
@@ -51,7 +63,6 @@
     >>> ls(sample_buildout, 'eggs')
     -  demo-0.2-py2.3.egg
     -  demoneeded-1.0-py2.3.egg
-    -  zc.recipe.egg.egg-link
 
 We see that we got an egg for demo that met the requirement, as well
 as the egg for demoneeded, wich demo requires.  (We also see an egg
@@ -94,7 +105,7 @@
     <BLANKLINE>
 
 The recipe gets the most recent distribution that satisfies the
-specification. For example, if we remove the restriction on demo:
+specification. For example, We remove the restriction on demo:
 
     >>> write(sample_buildout, 'buildout.cfg',
     ... """
@@ -104,9 +115,11 @@
     ... [demo]
     ... recipe = zc.recipe.egg
     ... find-links = %s
+    ... unzip = true
     ... """ % sample_eggs)
 
-and rerun the buildout:
+We also used the unzip uption to request a directory, rather than
+a zip file.
 
     >>> print system(runscript),
 
@@ -114,9 +127,8 @@
 
     >>> ls(sample_buildout, 'eggs')
     -  demo-0.2-py2.3.egg
-    -  demo-0.3-py2.3.egg
+    d  demo-0.3-py2.3.egg
     -  demoneeded-1.0-py2.3.egg
-    -  zc.recipe.egg.egg-link
 
 Note that we removed the distribution option, and the distribution
 defaulted to the part name.
@@ -150,7 +162,6 @@
 
 You can also control the name used for scripts:
 
-
     >>> write(sample_buildout, 'buildout.cfg',
     ... """
     ... [buildout]
@@ -167,3 +178,4 @@
     >>> ls(sample_buildout, 'bin')
     -  buildout
     -  foo
+

Modified: zc.buildout/trunk/eggrecipe/src/zc/recipe/egg/egg.py
===================================================================
--- zc.buildout/trunk/eggrecipe/src/zc/recipe/egg/egg.py	2006-06-19 18:02:54 UTC (rev 68762)
+++ zc.buildout/trunk/eggrecipe/src/zc/recipe/egg/egg.py	2006-06-19 19:20:24 UTC (rev 68763)
@@ -16,7 +16,7 @@
 $Id$
 """
 
-import os
+import os, zipfile
 import zc.buildout.egglinker
 import zc.buildout.easy_install
 
@@ -41,12 +41,20 @@
         options['_e'] = buildout['buildout']['eggs-directory']
         options['_d'] = buildout['buildout']['develop-eggs-directory']
 
+        assert options.get('unzip') in ('true', 'false', None)
+
+        python = options.get('python', buildout['buildout']['python'])
+        options['executable'] = buildout[python]['executable']
+
     def install(self):
         options = self.options
         distribution = options.get('distribution', self.name)
+        
         zc.buildout.easy_install.install(
-            distribution, options['_e'], self.links)
+            distribution, options['_e'], self.links, options['executable'],
+            always_unzip=options.get('unzip') == 'true')
 
+        eggss = [options['_d'], options['_e']]                    
         scripts = options.get('scripts')
         if scripts or scripts is None:
             if scripts is not None:
@@ -56,6 +64,6 @@
                     for s in scripts
                     ])
             return zc.buildout.egglinker.scripts(
-                [distribution],
-                options['_b'], [options['_d'], options['_e']], scripts=scripts)
-            
+                [distribution], options['_b'], eggss,
+                scripts=scripts, executable=options['executable'])
+

Added: zc.buildout/trunk/eggrecipe/src/zc/recipe/egg/selecting-python.txt
===================================================================
--- zc.buildout/trunk/eggrecipe/src/zc/recipe/egg/selecting-python.txt	2006-06-19 18:02:54 UTC (rev 68762)
+++ zc.buildout/trunk/eggrecipe/src/zc/recipe/egg/selecting-python.txt	2006-06-19 19:20:24 UTC (rev 68763)
@@ -0,0 +1,143 @@
+Controlling which Python to use
+-------------------------------
+
+The following assumes that your $HOME/.buildout/default.cfg has
+python2.3 and python2.4 sections that define Python 2.3 and Python 2.4
+executables.
+
+We can specify the python to use by specifying the name of a section
+to read the Python executable from.  The default is the section
+defined by the python buildout option.
+
+We have a directory with some sample eggs:
+
+    >>> ls(sample_eggs)
+    -  demo-0.1-py2.3.egg
+    -  demo-0.1-py2.4.egg
+    -  demo-0.2-py2.3.egg
+    -  demo-0.2-py2.4.egg
+    -  demo-0.3-py2.3.egg
+    -  demo-0.3-py2.4.egg
+    -  demoneeded-1.0-py2.3.egg
+    -  demoneeded-1.0-py2.4.egg
+
+We have a sample buildout.  Let's update it's configuration file to
+install the demo package using Python 2.3. 
+
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... """
+    ... [buildout]
+    ... parts = demo
+    ... eggs-directory = eggs
+    ...
+    ... [demo]
+    ... recipe = zc.recipe.egg
+    ... distribution = demo <0.3
+    ... find-links = %s
+    ... python = python2.3
+    ... """ % sample_eggs)
+
+In our default.cfg file in the .buildout subdirectiry of our
+directory, we have something like::
+
+    [python2.3]
+    executable = /usr/bin/python
+
+    [python2.4]
+    executable = /usr/local/bin/python2.4
+
+(Of course, the paths will vary from system to system.)
+
+Now, if we run the buildout:
+
+    >>> import os
+    >>> os.chdir(sample_buildout)
+    >>> buildout = os.path.join(sample_buildout, 'bin', 'buildout')
+    >>> print system(buildout),
+
+we'll get the Python 2.3 eggs for demo and demoneeded:
+
+    >>> ls(sample_buildout, 'eggs')
+    -  demo-0.2-py2.3.egg
+    -  demoneeded-1.0-py2.3.egg
+ 
+And the generated scripts invoke Python 2.3:
+
+    >>> f = open(os.path.join(sample_buildout, 'bin', 'demo'))
+    >>> f.readline().strip() == '#!' + python2_3_executable
+    True
+    >>> print f.read(),
+    <BLANKLINE>
+    import sys
+    sys.path[0:0] = [
+      '/private/tmp/tmpOEtRO8sample-buildout/eggs/demo-0.2-py2.3.egg',
+      '/private/tmp/tmpOEtRO8sample-buildout/eggs/demoneeded-1.0-py2.3.egg'
+      ]
+    <BLANKLINE>
+    import eggrecipedemo
+    <BLANKLINE>
+    if __name__ == '__main__':
+        eggrecipedemo.main()
+
+    >>> f = open(os.path.join(sample_buildout, 'bin', 'py_demo'))
+    >>> f.readline().strip() == '#!' + python2_3_executable + ' -i'
+    True
+    >>> print f.read(),
+    <BLANKLINE>
+    import sys
+    sys.path[0:0] = [
+      '/tmp/tmpOBTxDMsample-buildout/eggs/demo-0.2-py2.3.egg',
+      '/tmp/tmpOBTxDMsample-buildout/eggs/demoneeded-1.0-py2.3.egg'
+      ]
+
+If we change the Python version to 2.4, we'll use Python 2.4 eggs:
+
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... """
+    ... [buildout]
+    ... parts = demo
+    ... eggs-directory = eggs
+    ...
+    ... [demo]
+    ... recipe = zc.recipe.egg
+    ... distribution = demo <0.3
+    ... find-links = %s
+    ... python = python2.4
+    ... """ % sample_eggs)
+
+    >>> print system(buildout),
+
+    >>> ls(sample_buildout, 'eggs')
+    -  demo-0.2-py2.3.egg
+    -  demo-0.2-py2.4.egg
+    -  demoneeded-1.0-py2.3.egg
+    -  demoneeded-1.0-py2.4.egg
+
+    >>> f = open(os.path.join(sample_buildout, 'bin', 'demo'))
+    >>> f.readline().strip() == '#!' + python2_4_executable
+    True
+    >>> print f.read(),
+    <BLANKLINE>
+    import sys
+    sys.path[0:0] = [
+      '/private/tmp/tmpOEtRO8sample-buildout/eggs/demo-0.2-py2.4.egg',
+      '/private/tmp/tmpOEtRO8sample-buildout/eggs/demoneeded-1.0-py2.4.egg'
+      ]
+    <BLANKLINE>
+    import eggrecipedemo
+    <BLANKLINE>
+    if __name__ == '__main__':
+        eggrecipedemo.main()
+
+    >>> f = open(os.path.join(sample_buildout, 'bin', 'py_demo'))
+    >>> f.readline().strip() == '#!' + python2_4_executable + ' -i'
+    True
+    >>> print f.read(),
+    <BLANKLINE>
+    import sys
+    sys.path[0:0] = [
+      '/tmp/tmpOBTxDMsample-buildout/eggs/demo-0.2-py2.4.egg',
+      '/tmp/tmpOBTxDMsample-buildout/eggs/demoneeded-1.0-py2.4.egg'
+      ]
+
+


Property changes on: zc.buildout/trunk/eggrecipe/src/zc/recipe/egg/selecting-python.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: zc.buildout/trunk/eggrecipe/src/zc/recipe/egg/tests.py
===================================================================
--- zc.buildout/trunk/eggrecipe/src/zc/recipe/egg/tests.py	2006-06-19 18:02:54 UTC (rev 68762)
+++ zc.buildout/trunk/eggrecipe/src/zc/recipe/egg/tests.py	2006-06-19 19:20:24 UTC (rev 68763)
@@ -26,15 +26,23 @@
 def setUp(test):
     zc.buildout.testing.buildoutSetUp(test)
     open(os.path.join(test.globs['sample_buildout'],
-                      'eggs', 'zc.recipe.egg.egg-link'),
+                      'develop-eggs', 'zc.recipe.egg.egg-link'),
          'w').write(dirname(__file__, 4))
     zc.buildout.testing.create_sample_eggs(test)
         
 def tearDown(test):
     shutil.rmtree(test.globs['_sample_eggs_container'])
     zc.buildout.testing.buildoutTearDown(test)
+
+def setUpPython(test):
+    zc.buildout.testing.buildoutSetUp(test, clear_home=False)
     
+    open(os.path.join(test.globs['sample_buildout'],
+                      'develop-eggs', 'zc.recipe.egg.egg-link'),
+         'w').write(dirname(__file__, 4))
 
+    zc.buildout.testing.multi_python(test)
+
 def test_suite():
     return unittest.TestSuite((
         #doctest.DocTestSuite(),
@@ -49,7 +57,15 @@
                 '\\2-VVV-egg')
                ])
             ),
-        
+        doctest.DocFileSuite(
+            'selecting-python.txt',
+            setUp=setUpPython, tearDown=tearDown,
+            checker=renormalizing.RENormalizing([
+               (re.compile('\S+sample-(\w+)%s(\S+)' % os.path.sep),
+                r'/sample-\1/\2'),
+               (re.compile('\S+sample-(\w+)'), r'/sample-\1'),
+               ]),
+            ),        
         ))
 
 if __name__ == '__main__':

Modified: zc.buildout/trunk/src/zc/buildout/easy_install.py
===================================================================
--- zc.buildout/trunk/src/zc/buildout/easy_install.py	2006-06-19 18:02:54 UTC (rev 68762)
+++ zc.buildout/trunk/src/zc/buildout/easy_install.py	2006-06-19 19:20:24 UTC (rev 68763)
@@ -22,11 +22,16 @@
 
 import os, sys
 
-def install(spec, dest, links, python=sys.executable):
+def install(spec, dest, links, executable=sys.executable, always_unzip=False):
     prefix = sys.exec_prefix + os.path.sep
     path = os.pathsep.join([p for p in sys.path if not p.startswith(prefix)])
-    os.spawnle(
-        os.P_WAIT, python, python,
+    args = (
         '-c', 'from setuptools.command.easy_install import main; main()',
-        '-mqxd', dest, '-f', ' '.join(links), spec,
-        dict(PYTHONPATH=path))
+        '-mqxd', dest)
+    if links:
+        args += ('-f', ' '.join(links))
+    if always_unzip:
+        args += ('-Z', )
+    args += (spec, dict(PYTHONPATH=path))
+    
+    os.spawnle(os.P_WAIT, executable, executable, *args)

Modified: zc.buildout/trunk/src/zc/buildout/easy_install.txt
===================================================================
--- zc.buildout/trunk/src/zc/buildout/easy_install.txt	2006-06-19 18:02:54 UTC (rev 68762)
+++ zc.buildout/trunk/src/zc/buildout/easy_install.txt	2006-06-19 19:20:24 UTC (rev 68763)
@@ -20,9 +20,13 @@
 
     >>> ls(sample_eggs)
     -  demo-0.1-py2.3.egg
+    -  demo-0.1-py2.4.egg
     -  demo-0.2-py2.3.egg
+    -  demo-0.2-py2.4.egg
     -  demo-0.3-py2.3.egg
+    -  demo-0.3-py2.4.egg
     -  demoneeded-1.0-py2.3.egg
+    -  demoneeded-1.0-py2.4.egg
 
 let's make directory and install the demo egg to it:
 
@@ -34,3 +38,28 @@
     -  demo-0.3-py2.3.egg
     -  demoneeded-1.0-py2.3.egg
 
+We can specify an alternate Python executable, and we can specify
+that, when we retrieve (or create) an egg, it should be unzipped.
+
+    >>> import shutil
+    >>> shutil.rmtree(dest)
+    >>> dest = tempfile.mkdtemp()
+    >>> zc.buildout.easy_install.install(
+    ...     'demo', dest, [sample_eggs],
+    ...     always_unzip=True, executable= python2_3_executable)
+
+    >>> ls(dest)
+    d  demo-0.3-py2.3.egg
+    d  demoneeded-1.0-py2.3.egg
+
+    >>> shutil.rmtree(dest)
+    >>> dest = tempfile.mkdtemp()
+    >>> zc.buildout.easy_install.install(
+    ...     'demo', dest, [sample_eggs],
+    ...     always_unzip=True, executable= python2_4_executable)
+
+    >>> ls(dest)
+    d  demo-0.3-py2.4.egg
+    d  demoneeded-1.0-py2.4.egg
+
+

Modified: zc.buildout/trunk/src/zc/buildout/egglinker.py
===================================================================
--- zc.buildout/trunk/src/zc/buildout/egglinker.py	2006-06-19 18:02:54 UTC (rev 68762)
+++ zc.buildout/trunk/src/zc/buildout/egglinker.py	2006-06-19 19:20:24 UTC (rev 68763)
@@ -25,28 +25,44 @@
 # XXX need to deal with extras
 
 import os
+import re
 import sys
 
 import pkg_resources
 
-def distributions(reqs, eggss):
-    env = pkg_resources.Environment(eggss)
+_versions = {sys.executable: '%d.%d' % sys.version_info[:2]}
+def _get_version(executable):
+    try:
+        return _versions[executable]
+    except KeyError:
+        i, o = os.popen4(executable + ' -V')
+        i.close()
+        version = o.read().strip()
+        o.close()
+        pystring, version = version.split()
+        assert pystring == 'Python'
+        version = re.match('(\d[.]\d)[.]\d$', version).group(1)
+        _versions[executable] = version
+        return version
+
+def distributions(reqs, eggss, executable=sys.executable):
+    env = pkg_resources.Environment(eggss, python=_get_version(executable))
     ws = pkg_resources.WorkingSet()
     reqs = [pkg_resources.Requirement.parse(r) for r in reqs]
     return ws.resolve(reqs, env=env)
 
-def path(reqs, eggss):
-    dists = distributions(reqs, eggss)
+def path(reqs, eggss, executable=sys.executable):
+    dists = distributions(reqs, eggss, executable)
     return [dist.location for dist in dists]
 
-def location(spec, eggss):
-    env = pkg_resources.Environment(eggss)
+def location(spec, eggss, executable=sys.executable):
+    env = pkg_resources.Environment(eggss, python=_get_version(executable))
     req = pkg_resources.Requirement.parse(spec)
     dist = env.best_match(req, pkg_resources.WorkingSet())
     return dist.location    
 
-def scripts(reqs, dest, eggss, scripts=None):
-    dists = distributions(reqs, eggss)
+def scripts(reqs, dest, eggss, scripts=None, executable=sys.executable):
+    dists = distributions(reqs, eggss, executable)
     reqs = [pkg_resources.Requirement.parse(r) for r in reqs]
     projects = [r.project_name for r in reqs]
     path = "',\n  '".join([dist.location for dist in dists])
@@ -64,7 +80,7 @@
 
                 sname = os.path.join(dest, sname)
                 generated.append(sname)
-                _script(dist, 'console_scripts', name, path, sname)
+                _script(dist, 'console_scripts', name, path, sname, executable)
 
             name = 'py_'+dist.project_name
             if scripts is not None:
@@ -75,14 +91,14 @@
             if sname is not None:
                 sname = os.path.join(dest, sname)
                 generated.append(sname)
-                _pyscript(path, sname)
+                _pyscript(path, sname, executable)
 
     return generated
 
-def _script(dist, group, name, path, dest):
+def _script(dist, group, name, path, dest, executable):
     entry_point = dist.get_entry_info(group, name)
     open(dest, 'w').write(script_template % dict(
-        python = sys.executable,
+        python = executable,
         path = path,
         project = dist.project_name,
         name = name,
@@ -109,9 +125,9 @@
 '''
 
 
-def _pyscript(path, dest):
+def _pyscript(path, dest, executable):
     open(dest, 'w').write(py_script_template % dict(
-        python = sys.executable,
+        python = executable,
         path = path,
         ))
     try:

Modified: zc.buildout/trunk/src/zc/buildout/egglinker.txt
===================================================================
--- zc.buildout/trunk/src/zc/buildout/egglinker.txt	2006-06-19 18:02:54 UTC (rev 68762)
+++ zc.buildout/trunk/src/zc/buildout/egglinker.txt	2006-06-19 19:20:24 UTC (rev 68763)
@@ -17,9 +17,13 @@
 
     >>> ls(sample_eggs)
     -  demo-0.1-py2.3.egg
+    -  demo-0.1-py2.4.egg
     -  demo-0.2-py2.3.egg
+    -  demo-0.2-py2.4.egg
     -  demo-0.3-py2.3.egg
+    -  demo-0.3-py2.4.egg
     -  demoneeded-1.0-py2.3.egg
+    -  demoneeded-1.0-py2.4.egg
 
 The demo package depends on the demoneeded package.
 
@@ -66,8 +70,8 @@
     <BLANKLINE>
     import sys
     sys.path[0:0] = [
-      '/tmp/tmpuR5-n7eggtest/dist/demo-0.1-py2.3.egg',
-      '/tmp/tmpuR5-n7eggtest/dist/demoneeded-1.0-py2.3.egg'
+      '/tmp/xyzsample-eggs/demo-0.1-py2.3.egg',
+      '/tmp/xyzsample-eggs/demoneeded-1.0-py2.3.egg'
       ]
     <BLANKLINE>
     import eggrecipedemo
@@ -90,8 +94,8 @@
     <BLANKLINE>
     import sys
     sys.path[0:0] = [
-      '/tmp/tmpuR5-n7eggtest/dist/demo-0.1-py2.3.egg',
-      '/tmp/tmpuR5-n7eggtest/dist/demoneeded-1.0-py2.3.egg'
+      '/tmp/xyzsample-eggs/demo-0.1-py2.3.egg',
+      '/tmp/xyzsample-eggs/demoneeded-1.0-py2.3.egg'
       ]
 
 An additional argumnet can be passed to define which scripts to install
@@ -112,8 +116,6 @@
     >>> print system(os.path.join(bin, 'run')),
     1 1
 
-    >>> shutil.rmtree(bin)
-
 Sometimes we need more control over script generation.  Some
 lower-level APIs are available to help us generate scripts ourselves.
 These apis are a little bit higher level than those provided by
@@ -124,15 +126,15 @@
 
     >>> zc.buildout.egglinker.path(['demo==0.1'], [sample_eggs])
     ... # doctest: +NORMALIZE_WHITESPACE
-    ['/tmp/tmpQeJjpkeggtest/dist/demo-0.1-py2.4.egg', 
-     '/tmp/tmpQeJjpkeggtest/dist/demoneeded-1.0-py2.4.egg']
+    ['/tmp/xyzsample-eggs/demo-0.1-py2.3.egg', 
+     '/tmp/xyzsample-eggs/demoneeded-1.0-py2.3.egg']
 
 
 The location method returns the distribution location for an egg that
 satisfies a requirement:
 
     >>> zc.buildout.egglinker.location('demo==0.1', [sample_eggs])
-    '/tmp/tmpQeJjpkeggtest/dist/demo-0.1-py2.4.egg'
+    '/tmp/xyzsample-eggs/demo-0.1-py2.3.egg'
 
 The distributions function can retrieve a list of distributions found
 ineg directories that match a sequence of requirements:
@@ -141,3 +143,73 @@
     ...  zc.buildout.egglinker.distributions(['demo==0.1'], [sample_eggs])]
     [('demo', '0.1'), ('demoneeded', '1.0')]
 
+Using a custom Python interpreter
+---------------------------------
+
+You can pass an executable argument to egglinker methods:
+
+    >>> scripts = zc.buildout.egglinker.scripts(
+    ...      ['demo==0.1'], bin, [sample_eggs], 
+    ...       executable=python2_3_executable)
+
+    >>> f = open(os.path.join(bin, 'demo'))
+    >>> f.readline().strip() == '#!' + python2_3_executable
+    True
+    >>> print f.read(),
+    <BLANKLINE>
+    import sys
+    sys.path[0:0] = [
+      '/tmp/sample-eggs/dist/demo-0.1-py2.3.egg',
+      '/tmp/sample-eggs/dist/demoneeded-1.0-py2.3.egg'
+      ]
+    <BLANKLINE>
+    import eggrecipedemo
+    <BLANKLINE>
+    if __name__ == '__main__':
+        eggrecipedemo.main()
+
+    >>> zc.buildout.egglinker.path(['demo==0.1'], [sample_eggs], 
+    ...                            python2_3_executable)
+    ... # doctest: +NORMALIZE_WHITESPACE
+    ['/tmp/sample-eggs/dist/demo-0.1-py2.3.egg', 
+     '/tmp/sample-eggs/dist/demoneeded-1.0-py2.3.egg']
+
+
+    >>> zc.buildout.egglinker.location('demo==0.1', [sample_eggs],
+    ...                                python2_3_executable)
+    '/tmp/sample-eggs/demo-0.1-py2.3.egg'
+
+    >>> [(d.project_name, d.version) for d in 
+    ...  zc.buildout.egglinker.distributions(
+    ...      ['demo==0.1'], [sample_eggs], python2_3_executable)]
+    [('demo', '0.1'), ('demoneeded', '1.0')]
+
+
+    >>> scripts = zc.buildout.egglinker.scripts(
+    ...      ['demo==0.1'], bin, [sample_eggs], 
+    ...       executable=python2_4_executable)
+
+    >>> f = open(os.path.join(bin, 'demo'))
+    >>> f.readline().strip() == '#!' + python2_4_executable
+    True
+    >>> print f.read(),
+    <BLANKLINE>
+    import sys
+    sys.path[0:0] = [
+      '/tmp/sample-eggs/dist/demo-0.1-py2.4.egg',
+      '/tmp/sample-eggs/dist/demoneeded-1.0-py2.4.egg'
+      ]
+    <BLANKLINE>
+    import eggrecipedemo
+    <BLANKLINE>
+    if __name__ == '__main__':
+        eggrecipedemo.main()
+
+    >>> zc.buildout.egglinker.path(['demo==0.1'], [sample_eggs], 
+    ...                            python2_4_executable)
+    ... # doctest: +NORMALIZE_WHITESPACE
+    ['/tmp/sample-eggs/dist/demo-0.1-py2.4.egg', 
+     '/tmp/sample-eggs/dist/demoneeded-1.0-py2.4.egg']
+
+    >>> shutil.rmtree(bin)
+

Modified: zc.buildout/trunk/src/zc/buildout/testing.py
===================================================================
--- zc.buildout/trunk/src/zc/buildout/testing.py	2006-06-19 18:02:54 UTC (rev 68762)
+++ zc.buildout/trunk/src/zc/buildout/testing.py	2006-06-19 19:20:24 UTC (rev 68763)
@@ -11,12 +11,12 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""XXX short summary goes here.
+"""Various test-support utility functions
 
 $Id$
 """
 
-import os, re, shutil, sys, tempfile, unittest
+import ConfigParser, os, re, shutil, sys, tempfile, unittest
 from zope.testing import doctest, renormalizing
 import pkg_resources
 
@@ -52,10 +52,11 @@
     i.close()
     return o.read()
 
-def buildoutSetUp(test):
-    # we both need to make sure that HOME isn't set and be prepared
-    # to restore whatever it was after the test.
-    test.globs['_oldhome'] = os.environ.pop('HOME', None)
+def buildoutSetUp(test, clear_home=True):
+    if clear_home:
+        # we both need to make sure that HOME isn't set and be prepared
+        # to restore whatever it was after the test.
+        test.globs['_oldhome'] = os.environ.pop('HOME', None)
 
     sample = tempfile.mkdtemp('sample-buildout')
     for name in ('bin', 'eggs', 'develop-eggs', 'parts'):
@@ -96,7 +97,7 @@
 def buildoutTearDown(test):
     shutil.rmtree(test.globs['sample_buildout'])
     os.chdir(test.globs['__original_wd__'])
-    if test.globs['_oldhome'] is not None:
+    if test.globs.get('_oldhome') is not None:
         os.environ['HOME'] = test.globs['_oldhome']
 
 
@@ -110,12 +111,12 @@
 sys.exit(load_entry_point('zc.buildout', 'console_scripts', 'buildout')())
 '''
 
-def runsetup(d):
+def runsetup(d, executable):
     here = os.getcwd()
     try:
         os.chdir(d)
         os.spawnle(
-            os.P_WAIT, sys.executable, sys.executable,
+            os.P_WAIT, executable, executable,
             'setup.py', '-q', 'bdist_egg',
             {'PYTHONPATH': os.path.dirname(pkg_resources.__file__)},
             )
@@ -123,20 +124,25 @@
     finally:
         os.chdir(here)
 
-def create_sample_eggs(test):
-    sample = tempfile.mkdtemp('sample-eggs')
-    test.globs['_sample_eggs_container'] = sample
-    test.globs['sample_eggs'] = os.path.join(sample, 'dist')
-    write(sample, 'README.txt', '')
+def create_sample_eggs(test, executable=sys.executable):
+    if '_sample_eggs_container' in test.globs:
+        sample = test.globs['_sample_eggs_container']
+    else:
+        sample = tempfile.mkdtemp('sample-eggs')
+        test.globs['_sample_eggs_container'] = sample
+        test.globs['sample_eggs'] = os.path.join(sample, 'dist')
+        write(sample, 'README.txt', '')
+
     write(sample, 'eggrecipedemobeeded.py', 'y=1\n')
     write(
         sample, 'setup.py',
         "from setuptools import setup\n"
         "setup(name='demoneeded', py_modules=['eggrecipedemobeeded'],"
         " zip_safe=True, version='1.0')\n"
-        )
-    runsetup(sample)
+        )        
+    runsetup(sample, executable)
     os.remove(os.path.join(sample, 'eggrecipedemobeeded.py'))
+
     for i in (1, 2, 3):
         write(
             sample, 'eggrecipedemo.py',
@@ -152,4 +158,17 @@
             " entry_points={'console_scripts': ['demo = eggrecipedemo:main']},"
             " zip_safe=True, version='0.%s')\n" % i
             )
-        runsetup(sample)
+        runsetup(sample, executable)
+
+def multi_python(test):
+    defaults = ConfigParser.RawConfigParser()
+    defaults.readfp(open(os.path.join(os.environ['HOME'],
+                                      '.buildout', 'default.cfg')))
+    p23 = defaults.get('python2.3', 'executable')
+    p24 = defaults.get('python2.4', 'executable')
+    create_sample_eggs(test, executable=p23)
+    create_sample_eggs(test, executable=p24)
+    test.globs['python2_3_executable'] = p23
+    test.globs['python2_4_executable'] = p24
+    
+    

Modified: zc.buildout/trunk/src/zc/buildout/tests.py
===================================================================
--- zc.buildout/trunk/src/zc/buildout/tests.py	2006-06-19 18:02:54 UTC (rev 68762)
+++ zc.buildout/trunk/src/zc/buildout/tests.py	2006-06-19 19:20:24 UTC (rev 68763)
@@ -64,8 +64,8 @@
 '''    
 
 def linkerSetUp(test):
-    zc.buildout.testing.buildoutSetUp(test)
-    zc.buildout.testing.create_sample_eggs(test)
+    zc.buildout.testing.buildoutSetUp(test, clear_home=False)
+    zc.buildout.testing.multi_python(test)
         
 def linkerTearDown(test):
     shutil.rmtree(test.globs['_sample_eggs_container'])
@@ -75,6 +75,61 @@
     shutil.rmtree(test.globs['extensions'])
     shutil.rmtree(test.globs['home'])
     zc.buildout.testing.buildoutTearDown(test)
+
+
+class PythonNormalizing(renormalizing.RENormalizing):
+
+    def _transform(self, want, got):
+        if '/xyzsample-eggs/' in want:
+            got = got.replace('-py2.4.egg', '-py2.3.egg')
+            firstg = got.split('\n')[0]
+            firstw = want.split('\n')[0]
+            if firstg.startswith('#!') and firstw.startswith('#!'):
+                firstg = ' '.join(firstg.split()[1:])
+                got = firstg + '\n' + '\n'.join(got.split('\n')[1:])
+                firstw = ' '.join(firstw.split()[1:])
+                want = firstw + '\n' + '\n'.join(want.split('\n')[1:])
+        
+        for pattern, repl in self.patterns:
+            want = pattern.sub(repl, want)
+            got = pattern.sub(repl, got)
+
+        return want, got
+
+    def check_output(self, want, got, optionflags):
+        if got == want:
+            return True
+
+        want, got = self._transform(want, got)
+        if got == want:
+            return True
+            
+        return doctest.OutputChecker.check_output(self, want, got, optionflags)
+
+    def output_difference(self, example, got, optionflags):
+
+        want = example.want
+
+        # If want is empty, use original outputter. This is useful
+        # when setting up tests for the first time.  In that case, we
+        # generally use the differencer to display output, which we evaluate
+        # by hand.
+        if not want.strip():
+            return doctest.OutputChecker.output_difference(
+                self, example, got, optionflags)
+
+        # Dang, this isn't as easy to override as we might wish
+        original = want
+        want, got = self._transform(want, got)
+
+        # temporarily hack example with normalized want:
+        example.want = want
+        result = doctest.OutputChecker.output_difference(
+            self, example, got, optionflags)
+        example.want = original
+
+        return result
+
     
 def test_suite():
     return unittest.TestSuite((
@@ -86,22 +141,23 @@
                (re.compile('__buildout_signature__ = recipes-\S+'),
                 '__buildout_signature__ = recipes-SSSSSSSSSSS'),
                (re.compile('\S+sample-(\w+)%s(\S+)' % os.path.sep),
-                r'/sample-\1/\3'),
-               (re.compile('\S+sample-(\w+)'),
-                r'/sample-\1/\3'),
-               (re.compile('executable = \S+python\S*'), 'executable = python'),
+                r'/sample-\1/\2'),
+               (re.compile('\S+sample-(\w+)'), r'/sample-\1'),
+               (re.compile('executable = \S+python\S*'),
+                'executable = python'),
                ])
             ),
+        
         doctest.DocFileSuite(
             'egglinker.txt', 'easy_install.txt', 
             setUp=linkerSetUp, tearDown=linkerTearDown,
-            checker=renormalizing.RENormalizing([
-               (re.compile('(\S+[/%(sep)s]| )'
-                           '(\\w+-)[^ \t\n%(sep)s/]+.egg'
-                           % dict(sep=os.path.sep)
-                           ),
-                '\\2-VVV-egg'),
-               (re.compile('\S+%spython(\d.\d)?' % os.path.sep), 'python')
+
+            checker=PythonNormalizing([
+               (re.compile("'%(sep)s\S+sample-eggs%(sep)s(dist%(sep)s)?"
+                           % dict(sep=os.path.sep)),
+                '/sample-eggs/'),
+               (re.compile("(-  demo(needed)?-\d[.]\d-py)\d[.]\d[.]egg"),
+                '\\1V.V.egg'),
                ]),
             ),
         doctest.DocTestSuite(



More information about the Checkins mailing list