[Checkins] SVN: zc.buildout/branches/gary-4/ basic tests and implementation of stand-alone interpreter option
Gary Poster
gary.poster at canonical.com
Thu Dec 17 21:33:09 EST 2009
Log message for revision 106736:
basic tests and implementation of stand-alone interpreter option
Changed:
U zc.buildout/branches/gary-4/src/zc/buildout/easy_install.py
U zc.buildout/branches/gary-4/src/zc/buildout/testing.py
U zc.buildout/branches/gary-4/zc.recipe.egg_/setup.py
U zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/README.txt
U zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/__init__.py
U zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/egg.py
-=-
Modified: zc.buildout/branches/gary-4/src/zc/buildout/easy_install.py
===================================================================
--- zc.buildout/branches/gary-4/src/zc/buildout/easy_install.py 2009-12-18 01:40:41 UTC (rev 106735)
+++ zc.buildout/branches/gary-4/src/zc/buildout/easy_install.py 2009-12-18 02:33:08 UTC (rev 106736)
@@ -985,7 +985,7 @@
real_sitecustomize_path = _get_module_file(
executable, 'sitecustomize')
if real_sitecustomize_path:
- sitecustomize.write('execfile(%s)\n' % (real_sitecustomize_path,))
+ sitecustomize.write('execfile(%r)\n' % (real_sitecustomize_path,))
sitecustomize.close()
generated.append(sitecustomize_path)
# Write site.py.
@@ -1031,7 +1031,7 @@
os.chmod(script_name,0755)
except (AttributeError, os.error):
pass
- logger.info("Generated interpreter %r.", name)
+ logger.info("Generated interpreter %r.", full_name)
generated.append(script_name)
return generated
Modified: zc.buildout/branches/gary-4/src/zc/buildout/testing.py
===================================================================
--- zc.buildout/branches/gary-4/src/zc/buildout/testing.py 2009-12-18 01:40:41 UTC (rev 106735)
+++ zc.buildout/branches/gary-4/src/zc/buildout/testing.py 2009-12-18 02:33:08 UTC (rev 106736)
@@ -28,6 +28,7 @@
import subprocess
import sys
import tempfile
+import textwrap
import threading
import time
import urllib2
@@ -202,6 +203,24 @@
time.sleep(0.01)
raise ValueError('Timed out waiting for: '+label)
+def make_buildout():
+ # Create a basic buildout.cfg to avoid a warning from buildout:
+ open('buildout.cfg', 'w').write(
+ "[buildout]\nparts =\n"
+ )
+ # Use the buildout bootstrap command to create a buildout
+ zc.buildout.buildout.Buildout(
+ 'buildout.cfg',
+ [('buildout', 'log-level', 'WARNING'),
+ # trick bootstrap into putting the buildout develop egg
+ # in the eggs dir.
+ ('buildout', 'develop-eggs-directory', 'eggs'),
+ ]
+ ).bootstrap([])
+ # Create the develop-eggs dir, which didn't get created the usual
+ # way due to the trick above:
+ os.mkdir('develop-eggs')
+
def buildoutSetUp(test):
test.globs['__tear_downs'] = __tear_downs = []
@@ -255,34 +274,45 @@
sample = tmpdir('sample-buildout')
os.chdir(sample)
+ make_buildout()
- # Create a basic buildout.cfg to avoid a warning from buildout:
- open('buildout.cfg', 'w').write(
- "[buildout]\nparts =\n"
- )
-
- # Use the buildout bootstrap command to create a buildout
- zc.buildout.buildout.Buildout(
- 'buildout.cfg',
- [('buildout', 'log-level', 'WARNING'),
- # trick bootstrap into putting the buildout develop egg
- # in the eggs dir.
- ('buildout', 'develop-eggs-directory', 'eggs'),
- ]
- ).bootstrap([])
-
-
-
- # Create the develop-eggs dir, which didn't get created the usual
- # way due to the trick above:
- os.mkdir('develop-eggs')
-
def start_server(path):
port, thread = _start_server(path, name=path)
url = 'http://localhost:%s/' % port
register_teardown(lambda: stop_server(url, thread))
return url
+ def make_py(initialization='', site_packages_dir=None):
+ """Returns paths to new executable and to its site-packages.
+ """
+ buildout = tmpdir('executable_buildout')
+ if site_packages_dir is None:
+ site_packages_dir = mkdir(buildout, 'site-packages')
+ old_wd = os.getcwd()
+ os.chdir(buildout)
+ make_buildout()
+ initialization = '\n'.join(
+ ' ' + line for line in initialization.split('\n'))
+ install_develop(
+ 'zc.recipe.egg', os.path.join(buildout, 'develop-eggs'))
+ write('buildout.cfg', textwrap.dedent('''\
+ [buildout]
+ parts = py
+
+ [py]
+ recipe = zc.recipe.egg:interpreter
+ initialization =
+ %(initialization)s
+ extra-paths = %(site-packages)s
+ eggs = setuptools
+ ''') % {
+ 'initialization': initialization,
+ 'site-packages': site_packages_dir})
+ system(os.path.join(buildout, 'bin', 'buildout'))
+ os.chdir(old_wd)
+ return (
+ os.path.join(buildout, 'bin', 'py'), site_packages_dir)
+
test.globs.update(dict(
sample_buildout = sample,
ls = ls,
@@ -301,6 +331,7 @@
start_server = start_server,
buildout = os.path.join(sample, 'bin', 'buildout'),
wait_until = wait_until,
+ make_py = make_py
))
zc.buildout.easy_install.prefer_final(prefer_final)
Modified: zc.buildout/branches/gary-4/zc.recipe.egg_/setup.py
===================================================================
--- zc.buildout/branches/gary-4/zc.recipe.egg_/setup.py 2009-12-18 01:40:41 UTC (rev 106735)
+++ zc.buildout/branches/gary-4/zc.recipe.egg_/setup.py 2009-12-18 02:33:08 UTC (rev 106736)
@@ -76,6 +76,7 @@
'eggs = %s:Eggs' % name,
'custom = %s:Custom' % name,
'develop = %s:Develop' % name,
+ 'interpreter = %s:Interpreter' % name,
]
},
include_package_data = True,
Modified: zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/README.txt
===================================================================
--- zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/README.txt 2009-12-18 01:40:41 UTC (rev 106735)
+++ zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/README.txt 2009-12-18 02:33:08 UTC (rev 106736)
@@ -154,6 +154,8 @@
interpreter
The name of a script to generate that allows access to a Python
interpreter that has the path set based on the eggs installed.
+ See the ``interpreter`` (or ``py``) recipe, below, for a more
+ full-featured interpreter.
extra-paths
Extra paths to include in a generated script.
@@ -750,3 +752,218 @@
Uninstalling bigdemo.
Installing demo.
Generated script '/sample-buildout/bin/foo'.
+
+Interpreter generation
+----------------------
+
+What if you want a more full-featured interpreter than the one described
+above? That one is a script that mimics an interpreter--it has support
+for only a limited number of command-line options.
+
+The interpreter recipe generates a full-fledged version. Here's an example.
+
+ >>> write(sample_buildout, 'buildout.cfg',
+ ... """
+ ... [buildout]
+ ... parts = py
+ ...
+ ... [py]
+ ... recipe = zc.recipe.egg:interpreter
+ ... eggs = demo<0.3
+ ... find-links = %(server)s
+ ... index = %(server)s/index
+ ... """ % dict(server=link_server))
+
+ >>> print system(buildout),
+ Uninstalling demo.
+ Installing py.
+ Generated interpreter '/sample-buildout/bin/py'.
+
+Notice that the recipe took the name of the recipe from the name of the
+section.
+
+The bin/py script now just restarts Python after specifying a special
+path in PYTHONPATH.
+
+ >>> cat(sample_buildout, 'bin', 'py') # doctest: +NORMALIZE_WHITESPACE
+ #!/usr/bin/python2.4 -S
+ <BLANKLINE>
+ import os
+ import sys
+ <BLANKLINE>
+ argv = [sys.executable] + sys.argv[1:]
+ environ = os.environ.copy()
+ path = '/sample-buildout/parts/py'
+ if environ.get('PYTHONPATH'):
+ path = os.pathsep.join([path, environ['PYTHONPATH']])
+ environ['PYTHONPATH'] = path
+ os.execve(sys.executable, argv, environ)
+
+The path is a directory that contains two files: our own site.py and
+sitecustomize.py.
+
+ >>> ls(sample_buildout, 'parts', 'py')
+ - site.py
+ - sitecustomize.py
+
+ >>> cat(sample_buildout, 'parts', 'py', 'site.py')
+ ... # doctest: +NORMALIZE_WHITESPACE
+ import sys
+ sys.path[0:0] = [
+ '/sample-buildout/eggs/demo-0.2-py2.4.egg',
+ '/sample-buildout/eggs/demoneeded-1.2c1-py2.4.egg',
+ ]
+ import sitecustomize
+
+ >>> cat(sample_buildout, 'parts', 'py', 'sitecustomize.py')
+
+Here's an example of using the generated interpreter.
+
+ >>> print system(join(sample_buildout, 'bin', 'py') +
+ ... ' -c "import sys, pprint; pprint.pprint(sys.path[:3])"')
+ ['',
+ '/sample-buildout/eggs/demo-0.2-py2.4.egg',
+ '/sample-buildout/eggs/demoneeded-1.2c1-py2.4.egg']
+ <BLANKLINE>
+
+The interpreter recipe takes several options. First, here's the list of the
+options that overlap from the scripts recipe. After this, we'll list the new
+options and describe them.
+
+* eggs
+* find-links
+* index
+* python
+* extra-paths
+* initialization
+* relative-paths
+* include-site-packages
+
+In addition to these, the interpreter script offers these three new options.
+
+include-site-customization
+ Normally the Python's real sitecustomize module is not processed.
+ If you want it to be processed, set this value to 'true'. This will
+ be honored irrespective of the setting for include-site-paths.
+
+extends
+ You can extend another section using this value. It is intended to be
+ used by extending a section that uses this package's scripts recipe.
+ In this manner, you can avoid repeating yourself.
+
+name
+ If you do not want to have the interpreter have the same name as the
+ section, you can set it explicitly with this option.
+
+Let's look at the ``extends`` option first.
+
+ >>> write(sample_buildout, 'buildout.cfg',
+ ... """
+ ... [buildout]
+ ... parts = demo python
+ ...
+ ... [demo]
+ ... recipe = zc.recipe.egg
+ ... eggs = demo<0.3
+ ... find-links = %(server)s
+ ... index = %(server)s/index
+ ...
+ ... [python]
+ ... recipe = zc.recipe.egg:interpreter
+ ... extends = demo
+ ... """ % dict(server=link_server))
+
+That's not quite as short as adding an "interpreter = py" option to the
+[demo] section, but an improvement over what it could be.
+
+Now let's put it in action.
+
+ >>> print system(buildout),
+ Uninstalling py.
+ Installing demo.
+ Generated script '/sample-buildout/bin/demo'.
+ Installing python.
+ Generated interpreter '/sample-buildout/bin/python'.
+
+ >>> print system(join(sample_buildout, 'bin', 'python') +
+ ... ' -c "import sys, pprint; pprint.pprint(sys.path[:3])"')
+ ['',
+ '/sample-buildout/eggs/demo-0.2-py2.4.egg',
+ '/sample-buildout/eggs/demoneeded-1.2c1-py2.4.egg']
+ <BLANKLINE>
+
+Note that the parts/py directory has been cleaned up, and parts/python has
+been created.
+
+ >>> ls(sample_buildout, 'parts')
+ d python
+
+Now let's use the include-site-customization option. It simply lets Python's
+underlying sitecustomize module, if it exists, be executed.
+
+To show this, we need a Python executable guaranteed to have a sitecustomize
+module. We'll make one. The os.environ change below will go into the
+sitecustomize. We'll be able to use that as a flag.
+
+ >>> py_path, site_packages_path = make_py(initialization='''\
+ ... import os
+ ... os.environ['zc.buildout'] = 'foo bar baz shazam'
+ ... ''')
+
+ >>> write(sample_buildout, 'buildout.cfg',
+ ... """
+ ... [buildout]
+ ... parts = py
+ ... executable = %(py_path)s
+ ...
+ ... [py]
+ ... recipe = zc.recipe.egg:interpreter
+ ... include-site-customization = true
+ ... eggs = demo<0.3
+ ... find-links = %(server)s
+ ... index = %(server)s/index
+ ... """ % dict(server=link_server, py_path=py_path))
+
+ >>> print system(buildout),
+ Uninstalling python.
+ Uninstalling demo.
+ Installing py.
+ Generated interpreter '/sample-buildout/bin/py'.
+
+ >>> cat(sample_buildout, 'parts', 'py', 'sitecustomize.py')
+ ... # doctest: +NORMALIZE_WHITESPACE
+ execfile('/executable_buildout/parts/py/sitecustomize.py')
+ >>> print system(join(sample_buildout, 'bin', 'py') +
+ ... ''' -c "import os; print os.environ['zc.buildout']"''')
+ foo bar baz shazam
+ <BLANKLINE>
+
+The last new option is ``name``. This simply changes the name of the
+interpreter, so that you are not forced to use the name of the section.
+
+ >>> write(sample_buildout, 'buildout.cfg',
+ ... """
+ ... [buildout]
+ ... parts = interpreter
+ ...
+ ... [interpreter]
+ ... name = python2
+ ... recipe = zc.recipe.egg:interpreter
+ ... include-site-customization = true
+ ... eggs = demo<0.3
+ ... find-links = %(server)s
+ ... index = %(server)s/index
+ ... """ % dict(server=link_server))
+
+ >>> print system(buildout),
+ Uninstalling py.
+ Installing interpreter.
+ Generated interpreter '/sample-buildout/bin/python2'.
+
+ >>> print system(join(sample_buildout, 'bin', 'python2') +
+ ... ' -c "print 42"')
+ 42
+ <BLANKLINE>
+
+The other options have been described before for the scripts recipe, and so
+they will not be repeated here.
Modified: zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/__init__.py
===================================================================
--- zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/__init__.py 2009-12-18 01:40:41 UTC (rev 106735)
+++ zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/__init__.py 2009-12-18 02:33:08 UTC (rev 106736)
@@ -1,2 +1,2 @@
-from zc.recipe.egg.egg import Egg, Scripts, Eggs
+from zc.recipe.egg.egg import Egg, Scripts, Eggs, Interpreter
from zc.recipe.egg.custom import Custom, Develop
Modified: zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/egg.py
===================================================================
--- zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/egg.py 2009-12-18 01:40:41 UTC (rev 106735)
+++ zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/egg.py 2009-12-18 02:33:08 UTC (rev 106736)
@@ -19,6 +19,7 @@
import logging, os, re, zipfile
import zc.buildout.easy_install
+
class Eggs(object):
def __init__(self, buildout, name, options):
@@ -98,10 +99,11 @@
update = install
-class Scripts(Eggs):
+class ScriptBase(Eggs):
+
def __init__(self, buildout, name, options):
- super(Scripts, self).__init__(buildout, name, options)
+ super(ScriptBase, self).__init__(buildout, name, options)
b_options = buildout['buildout']
@@ -135,6 +137,9 @@
(value,))
self.include_site_packages = (value == 'true')
+
+class Scripts(ScriptBase):
+
parse_entry_point = re.compile(
'([^=]+)=(\w+(?:[.]\w+)*):(\w+(?:[.]\w+)*)$'
).match
@@ -184,6 +189,50 @@
update = install
+
+class Interpreter(ScriptBase):
+
+ def __init__(self, buildout, name, options):
+ if 'extends' in options:
+ options.update(buildout[options['extends']])
+ super(Interpreter, self).__init__(buildout, name, options)
+ b_options = buildout['buildout']
+ options['parts-directory'] = os.path.join(
+ b_options['parts-directory'], self.name)
+
+ value = options.setdefault(
+ 'include-site-customization',
+ b_options.get('include-site-customization', 'false'))
+ if value not in ('true', 'false'):
+ raise zc.buildout.UserError(
+ "Invalid value for include-site-customization option: %s" %
+ (value,))
+ self.include_site_customization = (value == 'true')
+
+ options.setdefault('name', name)
+
+ def install(self):
+ reqs, ws = self.working_set()
+ options = self.options
+ if not os.path.exists(options['parts-directory']):
+ os.mkdir(options['parts-directory'])
+ dir_made = True
+ else:
+ dir_made = False
+ generated = zc.buildout.easy_install.interpreter(
+ options['name'], ws, options['executable'],
+ options['bin-directory'], options['parts-directory'],
+ extra_paths=self.extra_paths,
+ initialization=options.get('initialization', ''),
+ relative_paths=self._relative_paths,
+ import_site=self.include_site_packages,
+ import_sitecustomize=self.include_site_customization,
+ )
+ if dir_made:
+ generated.append(options['parts-directory'])
+ return generated
+
+
def get_bool(options, name, default=False):
value = options.get(name)
if not value:
More information about the checkins
mailing list