Commit 2ba9d0b2 authored by amos's avatar amos

Merged amos-dependency-links branch to trunk (r80906:81181).

Now there's a buildout option (use-dependency-links) that allows you to 
disable setuptool dependency_links metadata.


git-svn-id: http://svn.zope.org/repos/main/zc.buildout/trunk@81182 62d5b8a3-27da-0310-9561-8e5933582275
parent e1df3174
...@@ -14,6 +14,22 @@ Change History ...@@ -14,6 +14,22 @@ Change History
1.0.0b31 (2007-???) 1.0.0b31 (2007-???)
===================== =====================
Feature Changes
---------------
- Added a configuration option that allows buildouts to ignore
dependency_links metadata specified in setup. By default
dependency_links in setup are used in addition to buildout specified
find-links. This can make it hard to control where eggs come
from. Here's how to tell buildout to ignore URLs in
dependency_links::
[buildout]
use-dependency-links = false
By default use-dependency-links is true, which matches the behavior
of previous versions of buildout.
Bugs Fixed Bugs Fixed
---------- ----------
......
...@@ -163,6 +163,12 @@ class Buildout(UserDict.DictMixin): ...@@ -163,6 +163,12 @@ class Buildout(UserDict.DictMixin):
prefer_final) prefer_final)
zc.buildout.easy_install.prefer_final(prefer_final=='true') zc.buildout.easy_install.prefer_final(prefer_final=='true')
use_dependency_links = options.get('use-dependency-links', 'true')
if use_dependency_links not in ('true', 'false'):
self._error('Invalid value for use-dependency-links option: %s',
use_dependency_links)
zc.buildout.easy_install.use_dependency_links(
use_dependency_links == 'true')
download_cache = options.get('download-cache') download_cache = options.get('download-cache')
if download_cache: if download_cache:
...@@ -642,7 +648,7 @@ class Buildout(UserDict.DictMixin): ...@@ -642,7 +648,7 @@ class Buildout(UserDict.DictMixin):
self['buildout']['eggs-directory'], self['buildout']['eggs-directory'],
links = self['buildout'].get('find-links', '').split(), links = self['buildout'].get('find-links', '').split(),
index = self['buildout'].get('index'), index = self['buildout'].get('index'),
path = [self['buildout']['develop-eggs-directory']], path = [self['buildout']['develop-eggs-directory']]
) )
upgraded = [] upgraded = []
......
...@@ -154,7 +154,7 @@ Currently, recipes must define 3 methods [#future_recipe_methods]_: ...@@ -154,7 +154,7 @@ Currently, recipes must define 3 methods [#future_recipe_methods]_:
The constructor is responsible for updating a parts options to reflect The constructor is responsible for updating a parts options to reflect
data read from other sections. The buildout system keeps track of data read from other sections. The buildout system keeps track of
whether a part specification has changed. A part specification has whether a part specification has changed. A part specification has
changed if it's options, after ajusting for data read from other changed if it's options, after adjusting for data read from other
sections, has changed, or if the recipe has changed. Only the options sections, has changed, or if the recipe has changed. Only the options
for the part are considered. If data are read from other sections, for the part are considered. If data are read from other sections,
then that information has to be reflected in the parts options. In then that information has to be reflected in the parts options. In
...@@ -174,7 +174,7 @@ has changed, then the part is uninstalled and reinstalled. The ...@@ -174,7 +174,7 @@ has changed, then the part is uninstalled and reinstalled. The
buildout only looks at the part's options, so any data used to buildout only looks at the part's options, so any data used to
configure the part needs to be reflected in the part's options. It is configure the part needs to be reflected in the part's options. It is
the job of a recipe constructor to make sure that the options include the job of a recipe constructor to make sure that the options include
all relevent data. all relevant data.
Of course, parts are also uninstalled if they are no-longer used. Of course, parts are also uninstalled if they are no-longer used.
...@@ -368,7 +368,7 @@ then raising a (or an instance of a subclass of a) ...@@ -368,7 +368,7 @@ then raising a (or an instance of a subclass of a)
zc.buildout.UserError exception. Raising an error other than a zc.buildout.UserError exception. Raising an error other than a
UserError still displays the error, but labels it as a bug in the UserError still displays the error, but labels it as a bug in the
buildout software or recipe. In the sample above, of someone gives a buildout software or recipe. In the sample above, of someone gives a
non-existant directory to create the directory in: non-existent directory to create the directory in:
>>> write(sample_buildout, 'buildout.cfg', >>> write(sample_buildout, 'buildout.cfg',
...@@ -464,7 +464,7 @@ leave previously created paths in place: ...@@ -464,7 +464,7 @@ leave previously created paths in place:
OSError: OSError:
[Errno 17] File exists: '/sample-buildout/bin' [Errno 17] File exists: '/sample-buildout/bin'
We meant to create a directiry bins, but typed bin. Now foo was We meant to create a directory bins, but typed bin. Now foo was
left behind. left behind.
>>> os.path.exists('foo') >>> os.path.exists('foo')
...@@ -582,7 +582,7 @@ It's critical that recipes clean up partial effects when errors ...@@ -582,7 +582,7 @@ It's critical that recipes clean up partial effects when errors
occur. Because recipes most commonly create files and directories, occur. Because recipes most commonly create files and directories,
buildout provides a helper API for removing created files when an buildout provides a helper API for removing created files when an
error occurs. Option objects have a created method that can be called error occurs. Option objects have a created method that can be called
to record files as they are created. If the install or update methof to record files as they are created. If the install or update method
returns with an error, then any registered paths are removed returns with an error, then any registered paths are removed
automatically. The method returns the files registered and can be automatically. The method returns the files registered and can be
used to return the files created. Let's use this API to simplify the used to return the files created. Let's use this API to simplify the
...@@ -633,7 +633,7 @@ returns the registered paths. We did this for illustrative purposes. ...@@ -633,7 +633,7 @@ returns the registered paths. We did this for illustrative purposes.
It would be simpler just to return the paths as before. It would be simpler just to return the paths as before.
If we rerun the buildout, again, we'll get the error and no If we rerun the buildout, again, we'll get the error and no
directiories will be created: directories will be created:
>>> print system(buildout), >>> print system(buildout),
Develop: '/sample-buildout/recipes' Develop: '/sample-buildout/recipes'
...@@ -685,7 +685,7 @@ extensions are: ...@@ -685,7 +685,7 @@ extensions are:
- option names are case sensitive - option names are case sensitive
- option values can ue a substitution syntax, described below, to - option values can use a substitution syntax, described below, to
refer to option values in specific sections. refer to option values in specific sections.
The ConfigParser syntax is very flexible. Section names can contain The ConfigParser syntax is very flexible. Section names can contain
...@@ -821,7 +821,7 @@ restriction might be relaxed in future releases. ...@@ -821,7 +821,7 @@ restriction might be relaxed in future releases.
Automatic part selection and ordering Automatic part selection and ordering
------------------------------------- -------------------------------------
When a section with a recipe is refered to, either through variable When a section with a recipe is referred to, either through variable
substitution or by an initializing recipe, the section is treated as a substitution or by an initializing recipe, the section is treated as a
part and added to the part list before the referencing part. For part and added to the part list before the referencing part. For
example, we can leave data-dir out of the parts list: example, we can leave data-dir out of the parts list:
...@@ -1076,7 +1076,7 @@ we'll set up a web server with some configuration files. ...@@ -1076,7 +1076,7 @@ we'll set up a web server with some configuration files.
recipe recipes:debug recipe recipes:debug
Here we specified a URL for the file we extended. The file we Here we specified a URL for the file we extended. The file we
downloaded, itself refered to a file on the server using a relative downloaded, itself referred to a file on the server using a relative
URL reference. Relative references are interpreted relative to the URL reference. Relative references are interpreted relative to the
base URL when they appear in configuration files loaded via URL. base URL when they appear in configuration files loaded via URL.
...@@ -1282,7 +1282,7 @@ When the buildout is run the service will be installed ...@@ -1282,7 +1282,7 @@ When the buildout is run the service will be installed
<BLANKLINE> <BLANKLINE>
The service has been installed. If the buildout is run again with no The service has been installed. If the buildout is run again with no
changes, the serivce shouldn't be changed. changes, the service shouldn't be changed.
>>> print system(buildout) >>> print system(buildout)
Develop: '/sample-buildout/recipes' Develop: '/sample-buildout/recipes'
...@@ -1424,7 +1424,7 @@ is run before the directory is deleted. ...@@ -1424,7 +1424,7 @@ is run before the directory is deleted.
recipe recipes:debug recipe recipes:debug
<BLANKLINE> <BLANKLINE>
Now we will return the registeration to normal for the benefit of the Now we will return the registration to normal for the benefit of the
rest of the examples. rest of the examples.
>>> write(sample_buildout, 'recipes', 'setup.py', >>> write(sample_buildout, 'recipes', 'setup.py',
...@@ -2004,7 +2004,7 @@ Note that a basic setup.cfg was created for us. ...@@ -2004,7 +2004,7 @@ Note that a basic setup.cfg was created for us.
- setuptools-0.6-py2.3.egg - setuptools-0.6-py2.3.egg
- zc.buildout-1.0-py2.3.egg - zc.buildout-1.0-py2.3.egg
(We list both the eggs and develop-eggs diectories because the (We list both the eggs and develop-eggs directories because the
buildout or setuptools egg could be installed in the develop-eggs buildout or setuptools egg could be installed in the develop-eggs
directory if the original buildout had develop eggs for either directory if the original buildout had develop eggs for either
buildout or setuptools.) buildout or setuptools.)
...@@ -2060,35 +2060,49 @@ running the buildout when not connected to the internet. It also ...@@ -2060,35 +2060,49 @@ running the buildout when not connected to the internet. It also
makes buildouts run much faster. This option is typically set using makes buildouts run much faster. This option is typically set using
the buildout -o option. the buildout -o option.
Prefering Final Releases Preferring Final Releases
------------------------ -------------------------
Currently, when searching for new releases, the newest available Currently, when searching for new releases, the newest available
release is used. This isn't usually ideal, as you may get development release is used. This isn't usually ideal, as you may get a
releaes or alpha releases not ready to be widely used. You can development release or alpha releases not ready to be widely used.
request that final releases be prefered using the prefer final option You can request that final releases be preferred using the prefer
in the buildout section:: final option in the buildout section::
[buildout] [buildout]
... ...
prefer-final = true prefer-final = true
When the prefer-final option is set to true, then when searching for When the prefer-final option is set to true, then when searching for
new releases, final releases are prefered. If there are final new releases, final releases are preferred. If there are final
releases that satisfy distribution requirements, then those releases releases that satisfy distribution requirements, then those releases
are used even if newer non-final releases are available. The buildout are used even if newer non-final releases are available. The buildout
prefer-final option can be used to override this behavior. prefer-final option can be used to override this behavior.
In buildout version 2, final releases will be prefered by default. In buildout version 2, final releases will be preferred by default.
You will then need to use a false value for prefer-final to get the You will then need to use a false value for prefer-final to get the
newset releases. newest releases.
Dependency links
----------------
By default buildout will obey the setuptools dependency_links metadata
when it looks for dependencies. This behavior can be controlled with
the use-dependency-links buildout option::
[buildout]
...
use-dependency-links = false
The option defaults to true. If you set it to false, then dependency
links are only looked for in the locations specified by find-links.
Controlling the installation database Controlling the installation database
------------------------------------- -------------------------------------
The buildout installed uption is used to specify the file used to save The buildout installed option is used to specify the file used to save
information on installed parts. This option is initialized to information on installed parts. This option is initialized to
".installed.cfg", but it can be overridded in the configuration file ".installed.cfg", but it can be overridden in the configuration file
or on the command line: or on the command line:
>>> write('buildout.cfg', >>> write('buildout.cfg',
...@@ -2119,7 +2133,7 @@ or on the command line: ...@@ -2119,7 +2133,7 @@ or on the command line:
d recipes d recipes
The installation database can be disabled by supplying an empty The installation database can be disabled by supplying an empty
buildout installed opttion: buildout installed option:
>>> os.remove('inst.cfg') >>> os.remove('inst.cfg')
>>> print system(buildout+' buildout:installed='), >>> print system(buildout+' buildout:installed='),
...@@ -2212,7 +2226,7 @@ egg to be built: ...@@ -2212,7 +2226,7 @@ egg to be built:
>>> print system(os.path.join(sample_bootstrapped, 'bin', 'buildout')), >>> print system(os.path.join(sample_bootstrapped, 'bin', 'buildout')),
Develop: '/sample-bootstrapped/demo' Develop: '/sample-bootstrapped/demo'
Now we can add the extensions option. We were a bit tricly and ran Now we can add the extensions option. We were a bit tricky and ran
the buildout once with the demo develop egg defined but without the the buildout once with the demo develop egg defined but without the
extension option. This is because extensions are loaded before the extension option. This is because extensions are loaded before the
buildout creates develop eggs. We needed to use a separate buildout buildout creates develop eggs. We needed to use a separate buildout
...@@ -2235,7 +2249,7 @@ We see that our extension is loaded and executed: ...@@ -2235,7 +2249,7 @@ We see that our extension is loaded and executed:
.. [#future_recipe_methods] In the future, additional mathods may be .. [#future_recipe_methods] In the future, additional methods may be
added. Older recipes with fewer methods will still be added. Older recipes with fewer methods will still be
supported. supported.
......
Dependency links
----------------
By default buildout will obey the setuptools dependency_links metadata
when it looks for dependencies. This behavior can be controlled with
the use-dependency-links buildout option.
[buildout]
...
use-dependency-links = false
The option defaults to true. If you set it to false, then dependency
links are only looked for in the locations specified by find-links.
Let's see this feature in action. To begin, let's create a new egg
repository. This repository uses the same sample eggs as the normal
testing repository.
>>> link_server2 = start_server(sample_eggs)
Turn on logging on this server so that we can see when eggs are pulled
from it.
>>> get(link_server2 + 'enable_server_logging')
GET 200 /enable_server_logging
''
Let's create a develop egg in our buildout that specifies
dependency_links which point to the new server.
>>> mkdir(sample_buildout, 'depdemo')
>>> write(sample_buildout, 'depdemo', 'dependencydemo.py',
... 'import eggrecipedemoneeded')
>>> write(sample_buildout, 'depdemo', 'setup.py',
... '''from setuptools import setup; setup(
... name='depdemo', py_modules=['dependencydemo'],
... install_requires = 'demoneeded',
... dependency_links = ['%s'],
... zip_safe=True, version='1')
... ''' % link_server2)
Now let's configure the buildout to use the develop egg.
>>> write(sample_buildout, 'buildout.cfg',
... '''
... [buildout]
... develop = depdemo
... parts = eggs
...
... [eggs]
... recipe = zc.recipe.egg:eggs
... eggs = depdemo
... ''')
Now we can run the buildout.
>>> print system(buildout)
GET 200 /
GET 200 /demoneeded-1.2c1.zip
Develop: '/sample-buildout/depdemo'
Installing eggs.
Getting distribution for 'demoneeded'.
Got demoneeded 1.2c1.
<BLANKLINE>
Notice that the egg was retrieved from the logging server.
Now let's change the egg so that it doesn't specify dependency links.
>>> write(sample_buildout, 'depdemo', 'setup.py',
... '''from setuptools import setup; setup(
... name='depdemo', py_modules=['dependencydemo'],
... install_requires = 'demoneeded',
... zip_safe=True, version='1')
... ''')
Now we'll remove the existing dependency egg, and rerunning the
buildout to see where the egg comes from this time.
>>> from glob import glob
>>> from os.path import join
>>> def remove_demoneeded_egg():
... for egg in glob(join(sample_buildout, 'eggs', 'demoneeded*.egg')):
... remove(sample_buildout, 'eggs', egg)
>>> remove_demoneeded_egg()
>>> print system(buildout)
Develop: '/sample-buildout/depdemo'
Updating eggs.
Couldn't find index page for 'demoneeded' (maybe misspelled?)
Getting distribution for 'demoneeded'.
While:
Updating eggs.
Getting distribution for 'demoneeded'.
Error: Couldn't find a distribution for 'demoneeded'.
<BLANKLINE>
Now it can't find the dependency since neither the buildout
configuration nor setup specifies where to look.
Let's change things so that the buildout configuration specifies where
to look for eggs.
>>> write(sample_buildout, 'buildout.cfg',
... '''
... [buildout]
... develop = depdemo
... parts = eggs
... find-links = %s
...
... [eggs]
... recipe = zc.recipe.egg:eggs
... eggs = depdemo
... ''' % link_server)
>>> print system(buildout)
Develop: '/sample-buildout/depdemo'
Installing eggs.
Getting distribution for 'demoneeded'.
Got demoneeded 1.2c1.
<BLANKLINE>
This time the dependency egg was found on the server without logging
configured.
Now let's change things once again so that both buildout and setup
specify different places to look for the dependency egg.
>>> write(sample_buildout, 'depdemo', 'setup.py',
... '''from setuptools import setup; setup(
... name='depdemo', py_modules=['dependencydemo'],
... install_requires = 'demoneeded',
... dependency_links = ['%s'],
... zip_safe=True, version='1')
... ''' % link_server2)
>>> remove_demoneeded_egg()
>>> print system(buildout) #doctest: +ELLIPSIS
GET 200 /...
Develop: '/sample-buildout/depdemo'
Updating eggs.
Getting distribution for 'demoneeded'.
Got demoneeded 1.2c1.
<BLANKLINE>
So when both setuptools and buildout specify places to search for
eggs, the dependency_links takes precedence over find-links.
There is a buildout option that you can specify to change this
behavior. It is the use-dependency-links option. This option defaults
to true. When you specify false for this option, buildout will ignore
dependency_links and only look for eggs using find-links.
Here is an example of using this option to disable dependency_links.
>>> write(sample_buildout, 'buildout.cfg',
... '''
... [buildout]
... develop = depdemo
... parts = eggs
... find-links = %s
... use-dependency-links = false
...
... [eggs]
... recipe = zc.recipe.egg:eggs
... eggs = depdemo
... ''' % link_server)
>>> remove_demoneeded_egg()
>>> print system(buildout)
Develop: '/sample-buildout/depdemo'
Updating eggs.
Getting distribution for 'demoneeded'.
Got demoneeded 1.2c1.
<BLANKLINE>
Notice that this time the egg isn't downloaded from the logging server.
If we set the option to true, things return to the way they were
before. The dependency's are looked for first in the logging server.
>>> write(sample_buildout, 'buildout.cfg',
... '''
... [buildout]
... develop = depdemo
... parts = eggs
... find-links = %s
... use-dependency-links = true
...
... [eggs]
... recipe = zc.recipe.egg:eggs
... eggs = depdemo
... ''' % link_server)
>>> remove_demoneeded_egg()
>>> print system(buildout) #doctest: +ELLIPSIS
GET 200 /...
Develop: '/sample-buildout/depdemo'
Updating eggs.
Getting distribution for 'demoneeded'.
Got demoneeded 1.2c1.
<BLANKLINE>
...@@ -115,7 +115,8 @@ class Installer: ...@@ -115,7 +115,8 @@ class Installer:
_download_cache = None _download_cache = None
_install_from_cache = False _install_from_cache = False
_prefer_final = True _prefer_final = True
_use_dependency_links = True
def __init__(self, def __init__(self,
dest=None, dest=None,
links=(), links=(),
...@@ -125,6 +126,7 @@ class Installer: ...@@ -125,6 +126,7 @@ class Installer:
path=None, path=None,
newest=True, newest=True,
versions=None, versions=None,
use_dependency_links=None,
): ):
self._dest = dest self._dest = dest
...@@ -135,8 +137,8 @@ class Installer: ...@@ -135,8 +137,8 @@ class Installer:
links = () links = ()
index = 'file://' + self._download_cache index = 'file://' + self._download_cache
if use_dependency_links is not None:
self._use_dependency_links = use_dependency_links
self._links = links = list(_fix_file_links(links)) self._links = links = list(_fix_file_links(links))
if self._download_cache and (self._download_cache not in links): if self._download_cache and (self._download_cache not in links):
links.insert(0, self._download_cache) links.insert(0, self._download_cache)
...@@ -503,10 +505,10 @@ class Installer: ...@@ -503,10 +505,10 @@ class Installer:
else: else:
dists = [dist] dists = [dist]
# XXX Need test for this
for dist in dists: for dist in dists:
if (dist.has_metadata('dependency_links.txt') if (dist.has_metadata('dependency_links.txt')
and not self._install_from_cache and not self._install_from_cache
and self._use_dependency_links
): ):
for link in dist.get_metadata_lines('dependency_links.txt'): for link in dist.get_metadata_lines('dependency_links.txt'):
link = link.strip() link = link.strip()
...@@ -709,12 +711,19 @@ def prefer_final(setting=None): ...@@ -709,12 +711,19 @@ def prefer_final(setting=None):
Installer._prefer_final = bool(setting) Installer._prefer_final = bool(setting)
return old return old
def use_dependency_links(setting=None):
old = Installer._use_dependency_links
if setting is not None:
Installer._use_dependency_links = bool(setting)
return old
def install(specs, dest, def install(specs, dest,
links=(), index=None, links=(), index=None,
executable=sys.executable, always_unzip=False, executable=sys.executable, always_unzip=False,
path=None, working_set=None, newest=True, versions=None): path=None, working_set=None, newest=True, versions=None,
use_dependency_links=None):
installer = Installer(dest, links, index, executable, always_unzip, path, installer = Installer(dest, links, index, executable, always_unzip, path,
newest, versions) newest, versions, use_dependency_links)
return installer.install(specs, working_set) return installer.install(specs, working_set)
......
...@@ -34,11 +34,11 @@ positional arguments: ...@@ -34,11 +34,11 @@ positional arguments:
- A destination directory to install to and to satisfy requirements - A destination directory to install to and to satisfy requirements
from. The destination directory can be None, in which case, no new from. The destination directory can be None, in which case, no new
distributions are downloaded and there will be an error if the distributions are downloaded and there will be an error if the
needed distributions can't be found amoung those already installed. needed distributions can't be found among those already installed.
It supports a number of optional keyword arguments: It supports a number of optional keyword arguments:
find-links links
A sequence of URLs, file names, or directories to look for A sequence of URLs, file names, or directories to look for
links to distributions. links to distributions.
...@@ -66,13 +66,13 @@ always_unzip ...@@ -66,13 +66,13 @@ always_unzip
directories even if they could be installed as zip files. directories even if they could be installed as zip files.
working_set working_set
An exsiting working set to be augmented with additional An existing working set to be augmented with additional
distributions, if necessary to satisfy requirements. This allows distributions, if necessary to satisfy requirements. This allows
you to call install multiple times, if necessary, to gather you to call install multiple times, if necessary, to gather
multiple sets of requirements. multiple sets of requirements.
newest newest
A boolian value indicating whether to search for new distributions A boolean value indicating whether to search for new distributions
when already-installed distributions meet the requirement. When when already-installed distributions meet the requirement. When
this is true, the default, and when the destination directory is this is true, the default, and when the destination directory is
not None, then the install function will search for the newest not None, then the install function will search for the newest
...@@ -83,6 +83,12 @@ versions ...@@ -83,6 +83,12 @@ versions
when selecting distributions. This can be used to specify a set of when selecting distributions. This can be used to specify a set of
distribution versions independent of other requirements. distribution versions independent of other requirements.
use_dependency_links
A flag indicating whether to search for dependencies using the
setup dependency_links metadata or not. If true, links are searched
for using dependency_links in preference to other
locations. Defaults to true.
The install method returns a working set containing the distributions The install method returns a working set containing the distributions
needed to meet the given requirements. needed to meet the given requirements.
...@@ -140,7 +146,7 @@ value for newest, no new distributions will be installed: ...@@ -140,7 +146,7 @@ value for newest, no new distributions will be installed:
- demo-0.2-py2.4.egg - demo-0.2-py2.4.egg
- demoneeded-1.1-py2.4.egg - demoneeded-1.1-py2.4.egg
If we leave off the newst option, we'll get an update for demo: If we leave off the newest option, we'll get an update for demo:
>>> ws = zc.buildout.easy_install.install( >>> ws = zc.buildout.easy_install.install(
... ['demo'], dest, links=[link_server], index=link_server+'index/') ... ['demo'], dest, links=[link_server], index=link_server+'index/')
...@@ -150,8 +156,8 @@ If we leave off the newst option, we'll get an update for demo: ...@@ -150,8 +156,8 @@ If we leave off the newst option, we'll get an update for demo:
- demoneeded-1.1-py2.4.egg - demoneeded-1.1-py2.4.egg
Note that we didn't get the newest versions available. There were Note that we didn't get the newest versions available. There were
release candidated for newer versions of both packages. By default, release candidates for newer versions of both packages. By default,
final releases are prefered. We can change this behavior using the final releases are preferred. We can change this behavior using the
prefer_final function: prefer_final function:
>>> zc.buildout.easy_install.prefer_final(False) >>> zc.buildout.easy_install.prefer_final(False)
...@@ -178,10 +184,9 @@ Let's put the setting back to the default. ...@@ -178,10 +184,9 @@ Let's put the setting back to the default.
>>> zc.buildout.easy_install.prefer_final(True) >>> zc.buildout.easy_install.prefer_final(True)
False False
We can supply additional distributions. We can also supply We can supply additional distributions. We can also supply
specifications for distributions that would normally be found via specifications for distributions that would normally be found via
dependencies. We might do this to specify a sprcific version. dependencies. We might do this to specify a specific version.
>>> ws = zc.buildout.easy_install.install( >>> ws = zc.buildout.easy_install.install(
... ['demo', 'other', 'demoneeded==1.0'], dest, ... ['demo', 'other', 'demoneeded==1.0'], dest,
...@@ -214,7 +219,6 @@ can be useful when debugging. ...@@ -214,7 +219,6 @@ can be useful when debugging.
>>> ls(dest) >>> ls(dest)
d demo-0.3-py2.4.egg d demo-0.3-py2.4.egg
d demoneeded-1.1-py2.4.egg d demoneeded-1.1-py2.4.egg
>>> rmdir(dest) >>> rmdir(dest)
>>> dest = tmpdir('sample-install') >>> dest = tmpdir('sample-install')
...@@ -226,10 +230,10 @@ can be useful when debugging. ...@@ -226,10 +230,10 @@ can be useful when debugging.
d demo-0.3-py2.4.egg d demo-0.3-py2.4.egg
d demoneeded-1.1-py2.4.egg d demoneeded-1.1-py2.4.egg
Specifying version information indepenent of requirements Specifying version information independent of requirements
--------------------------------------------------------- ---------------------------------------------------------
Sometimes it's useful to specify version information indepenent of Sometimes it's useful to specify version information independent of
normal requirements specifications. For example, a buildout may need normal requirements specifications. For example, a buildout may need
to lock down a set of versions, without having to put put version to lock down a set of versions, without having to put put version
numbers in setup files or part definitions. If a dictionary is passed numbers in setup files or part definitions. If a dictionary is passed
...@@ -244,7 +248,7 @@ then the versions numbers will be used. ...@@ -244,7 +248,7 @@ then the versions numbers will be used.
In this example, we specified a version for demoneeded, even though we In this example, we specified a version for demoneeded, even though we
didn't define a requirement for it. The versions specified apply to didn't define a requirement for it. The versions specified apply to
depenencies as well as the specified requirements. dependencies as well as the specified requirements.
If we specify a version that's incompatible with a requirement, then If we specify a version that's incompatible with a requirement, then
we'll get an error: we'll get an error:
...@@ -332,7 +336,125 @@ dictionary: ...@@ -332,7 +336,125 @@ dictionary:
>>> [d.version for d in ws] >>> [d.version for d in ws]
['0.3', '1.1'] ['0.3', '1.1']
Dependency links
----------------
Setuptools allows metadata that describes where to search for package
dependencies. This option is called dependency_links. Buildout has its
own notion of where to look for dependencies, but it also uses the
setup tools dependency_links information if it's available.
Let's demo this by creating an egg that specifies dependency_links.
To begin, let's create a new egg repository. This repository hold a
newer version of the 'demoneeded' egg than the sample repository does.
>>> repoloc = tmpdir('repo')
>>> from zc.buildout.tests import create_egg
>>> create_egg('demoneeded', '1.2', repoloc)
>>> link_server2 = start_server(repoloc)
Turn on logging on this server so that we can see when eggs are pulled
from it.
>>> get(link_server2 + 'enable_server_logging')
GET 200 /enable_server_logging
''
Now we can create an egg that specifies that its dependencies are
found on this server.
>>> repoloc = tmpdir('repo2')
>>> create_egg('hasdeps', '1.0', repoloc,
... install_requires = "'demoneeded'",
... dependency_links = [link_server2])
Let's add the egg to another repository.
>>> link_server3 = start_server(repoloc)
Now let's install the egg.
>>> example_dest = tmpdir('example-install')
>>> workingset = zc.buildout.easy_install.install(
... ['hasdeps'], example_dest,
... links=[link_server3], index=link_server3+'index/')
GET 200 /
GET 200 /demoneeded-1.2-pyN.N.egg
The server logs show that the dependency was retrieved from the server
specified in the dependency_links.
Now let's see what happens if we provide two different ways to retrieve
the dependencies.
>>> rmdir(example_dest)
>>> example_dest = tmpdir('example-install')
>>> workingset = zc.buildout.easy_install.install(
... ['hasdeps'], example_dest, index=link_server+'index/',
... links=[link_server, link_server3])
GET 200 /
GET 200 /demoneeded-1.2-pyN.N.egg
Once again the dependency is fetched from the logging server even
though it is also available from the non-logging server. This is
because the version on the logging server is newer and buildout
normally chooses the newest egg available.
If you wish to control where dependencies come from regardless of
dependency_links setup metadata use the 'use_dependency_links' option
to zc.buildout.easy_install.install().
>>> rmdir(example_dest)
>>> example_dest = tmpdir('example-install')
>>> workingset = zc.buildout.easy_install.install(
... ['hasdeps'], example_dest, index=link_server+'index/',
... links=[link_server, link_server3],
... use_dependency_links=False)
Notice that this time the dependency egg is not fetched from the
logging server. When you specify not to use dependency_links, eggs
will only be searched for using the links you explicitly provide.
Another way to control this option is with the
zc.buildout.easy_install.use_dependency_links() function. This
function sets the default behavior for the zc.buildout.easy_install()
function.
>>> zc.buildout.easy_install.use_dependency_links(False)
True
The function returns its previous setting.
>>> rmdir(example_dest)
>>> example_dest = tmpdir('example-install')
>>> workingset = zc.buildout.easy_install.install(
... ['hasdeps'], example_dest, index=link_server+'index/',
... links=[link_server, link_server3])
It can be overridden by passing a keyword argument to the install
function.
>>> rmdir(example_dest)
>>> example_dest = tmpdir('example-install')
>>> workingset = zc.buildout.easy_install.install(
... ['hasdeps'], example_dest, index=link_server+'index/',
... links=[link_server, link_server3],
... use_dependency_links=True)
GET 200 /demoneeded-1.2-pyN.N.egg
To return the dependency_links behavior to normal call the function again.
>>> zc.buildout.easy_install.use_dependency_links(True)
False
>>> rmdir(example_dest)
>>> example_dest = tmpdir('example-install')
>>> workingset = zc.buildout.easy_install.install(
... ['hasdeps'], example_dest, index=link_server+'index/',
... links=[link_server, link_server3])
GET 200 /demoneeded-1.2-pyN.N.egg
Script generation Script generation
----------------- -----------------
...@@ -426,7 +548,7 @@ strings: ...@@ -426,7 +548,7 @@ strings:
- An attribute expression for an entry point within the module. - An attribute expression for an entry point within the module.
For example, we could have passed antry point information directly For example, we could have passed entry point information directly
rather than passing a requirement: rather than passing a requirement:
>>> scripts = zc.buildout.easy_install.scripts( >>> scripts = zc.buildout.easy_install.scripts(
...@@ -507,7 +629,7 @@ the path set: ...@@ -507,7 +629,7 @@ the path set:
If invoked with a script name and arguments, it will run that script, instead. If invoked with a script name and arguments, it will run that script, instead.
An additional argumnet can be passed to define which scripts to install An additional argument can be passed to define which scripts to install
and to provide script names. The argument is a dictionary mapping and to provide script names. The argument is a dictionary mapping
original script names to new script names. original script names to new script names.
...@@ -530,7 +652,7 @@ original script names to new script names. ...@@ -530,7 +652,7 @@ original script names to new script names.
Including extra paths in scripts Including extra paths in scripts
-------------------------------- --------------------------------
We can pass a keyword argument, extra paths, to caue additional paths We can pass a keyword argument, extra paths, to cause additional paths
to be included in the a generated script: to be included in the a generated script:
>>> scripts = zc.buildout.easy_install.scripts( >>> scripts = zc.buildout.easy_install.scripts(
...@@ -641,7 +763,7 @@ index ...@@ -641,7 +763,7 @@ index
will make our examples run a little bit faster. will make our examples run a little bit faster.
executable executable
A path to a Python executable. Distributions will ne installed A path to a Python executable. Distributions will be installed
using this executable and will be for the matching Python version. using this executable and will be for the matching Python version.
path path
...@@ -649,7 +771,7 @@ path ...@@ -649,7 +771,7 @@ path
distributions. distributions.
newest newest
A boolian value indicating whether to search for new distributions A boolean value indicating whether to search for new distributions
when already-installed distributions meet the requirement. When when already-installed distributions meet the requirement. When
this is true, the default, and when the destination directory is this is true, the default, and when the destination directory is
not None, then the install function will search for the newest not None, then the install function will search for the newest
...@@ -772,7 +894,7 @@ get an updated egg: ...@@ -772,7 +894,7 @@ get an updated egg:
The versions option also influences the versions used. For example, The versions option also influences the versions used. For example,
if we specify a version for extdemo, then that will be used, even if we specify a version for extdemo, then that will be used, even
though it isn't the newest. Let's clean out the destimation directory though it isn't the newest. Let's clean out the destination directory
first: first:
>>> import os >>> import os
...@@ -812,7 +934,7 @@ build_ext ...@@ -812,7 +934,7 @@ build_ext
command when building extensions. command when building extensions.
executable executable
A path to a Python executable. Distributions will ne installed A path to a Python executable. Distributions will be installed
using this executable and will be for the matching Python version. using this executable and will be for the matching Python version.
We have a local directory containing the extdemo source: We have a local directory containing the extdemo source:
...@@ -966,7 +1088,7 @@ from the link server: ...@@ -966,7 +1088,7 @@ from the link server:
... always_unzip=True) ... always_unzip=True)
GET 200 /demo-0.3-py2.4.egg GET 200 /demo-0.3-py2.4.egg
Normally, the download cache is the prefered source of downloads, but Normally, the download cache is the preferred source of downloads, but
not the only one. not the only one.
Installing solely from a download cache Installing solely from a download cache
......
...@@ -97,7 +97,7 @@ number of names to the test namespace: ...@@ -97,7 +97,7 @@ number of names to the test namespace:
and: and:
>>> get(server_url+'enable_server_logging') >>> get(server_url+'disable_server_logging')
This can be useful to see how buildout is interacting with a This can be useful to see how buildout is interacting with a
server. server.
......
...@@ -2087,19 +2087,27 @@ need to make it to the download cache. ...@@ -2087,19 +2087,27 @@ need to make it to the download cache.
""" """
def create_egg(name, version, dest): def create_egg(name, version, dest, install_requires=None,
dependency_links=None):
d = tempfile.mkdtemp() d = tempfile.mkdtemp()
if dest=='available': if dest=='available':
extras = dict(x=['x']) extras = dict(x=['x'])
else: else:
extras = {} extras = {}
if dependency_links:
links = 'dependency_links = %s, ' % dependency_links
else:
links = ''
if install_requires:
requires = 'install_requires = %s, ' % install_requires
else:
requires = ''
try: try:
open(os.path.join(d, 'setup.py'), 'w').write( open(os.path.join(d, 'setup.py'), 'w').write(
'from setuptools import setup\n' 'from setuptools import setup\n'
'setup(name=%r, version=%r, extras_require=%r, zip_safe=True,\n' 'setup(name=%r, version=%r, extras_require=%r, zip_safe=True,\n'
' py_modules=["setup"]\n)' ' %s %s py_modules=["setup"]\n)'
% (name, str(version), extras) % (name, str(version), extras, requires, links)
) )
zc.buildout.testing.bdist_egg(d, sys.executable, os.path.abspath(dest)) zc.buildout.testing.bdist_egg(d, sys.executable, os.path.abspath(dest))
finally: finally:
...@@ -2314,9 +2322,7 @@ We get an error if we specify anything but true or false: ...@@ -2314,9 +2322,7 @@ We get an error if we specify anything but true or false:
""" """
# XXX Tests needed:
# Link added from package meta data
...@@ -2330,12 +2336,12 @@ def create_sample_eggs(test, executable=sys.executable): ...@@ -2330,12 +2336,12 @@ def create_sample_eggs(test, executable=sys.executable):
write(tmp, 'README.txt', '') write(tmp, 'README.txt', '')
for i in (0, 1, 2): for i in (0, 1, 2):
write(tmp, 'eggrecipedemobeeded.py', 'y=%s\n' % i) write(tmp, 'eggrecipedemoneeded.py', 'y=%s\n' % i)
c1 = i==2 and 'c1' or '' c1 = i==2 and 'c1' or ''
write( write(
tmp, 'setup.py', tmp, 'setup.py',
"from setuptools import setup\n" "from setuptools import setup\n"
"setup(name='demoneeded', py_modules=['eggrecipedemobeeded']," "setup(name='demoneeded', py_modules=['eggrecipedemoneeded'],"
" zip_safe=True, version='1.%s%s', author='bob', url='bob', " " zip_safe=True, version='1.%s%s', author='bob', url='bob', "
"author_email='bob')\n" "author_email='bob')\n"
% (i, c1) % (i, c1)
...@@ -2346,18 +2352,18 @@ def create_sample_eggs(test, executable=sys.executable): ...@@ -2346,18 +2352,18 @@ def create_sample_eggs(test, executable=sys.executable):
tmp, 'setup.py', tmp, 'setup.py',
"from setuptools import setup\n" "from setuptools import setup\n"
"setup(name='other', zip_safe=False, version='1.0', " "setup(name='other', zip_safe=False, version='1.0', "
"py_modules=['eggrecipedemobeeded'])\n" "py_modules=['eggrecipedemoneeded'])\n"
) )
zc.buildout.testing.bdist_egg(tmp, executable, dest) zc.buildout.testing.bdist_egg(tmp, executable, dest)
os.remove(os.path.join(tmp, 'eggrecipedemobeeded.py')) os.remove(os.path.join(tmp, 'eggrecipedemoneeded.py'))
for i in (1, 2, 3, 4): for i in (1, 2, 3, 4):
write( write(
tmp, 'eggrecipedemo.py', tmp, 'eggrecipedemo.py',
'import eggrecipedemobeeded\n' 'import eggrecipedemoneeded\n'
'x=%s\n' 'x=%s\n'
'def main(): print x, eggrecipedemobeeded.y\n' 'def main(): print x, eggrecipedemoneeded.y\n'
% i) % i)
c1 = i==4 and 'c1' or '' c1 = i==4 and 'c1' or ''
write( write(
...@@ -2564,7 +2570,7 @@ def test_suite(): ...@@ -2564,7 +2570,7 @@ def test_suite():
), ),
doctest.DocFileSuite( doctest.DocFileSuite(
'easy_install.txt', 'downloadcache.txt', 'easy_install.txt', 'downloadcache.txt', 'dependencylinks.txt',
setUp=easy_install_SetUp, setUp=easy_install_SetUp,
tearDown=zc.buildout.testing.buildoutTearDown, tearDown=zc.buildout.testing.buildoutTearDown,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment