Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
slapos.buildout
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
isaak yansane-sisk
slapos.buildout
Commits
e0aa402a
Commit
e0aa402a
authored
Nov 17, 2012
by
Jim Fulton
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Removed some extra indentation that slipped in at some point
parent
354f6536
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
1144 additions
and
1144 deletions
+1144
-1144
src/zc/buildout/easy_install.txt
src/zc/buildout/easy_install.txt
+1144
-1144
No files found.
src/zc/buildout/easy_install.txt
View file @
e0aa402a
...
@@ -107,1218 +107,1218 @@ We have a link server that has a number of eggs:
...
@@ -107,1218 +107,1218 @@ We have a link server that has a number of eggs:
<a href="other-1.0-py2.4.egg">other-1.0-py2.4.egg</a><br>
<a href="other-1.0-py2.4.egg">other-1.0-py2.4.egg</a><br>
</body></html>
</body></html>
Let's make a directory and install the demo egg to it, using the demo:
Let's make a directory and install the demo egg to it, using the demo:
>>> dest = tmpdir('sample-install')
>>> dest = tmpdir('sample-install')
>>> import zc.buildout.easy_install
>>> import zc.buildout.easy_install
>>> ws = zc.buildout.easy_install.install(
>>> ws = zc.buildout.easy_install.install(
... ['demo==0.2'], dest,
... ['demo==0.2'], dest,
... links=[link_server], index=link_server+'index/')
... links=[link_server], index=link_server+'index/')
We requested version 0.2 of the demo distribution to be installed into
We requested version 0.2 of the demo distribution to be installed into
the destination server. We specified that we should search for links
the destination server. We specified that we should search for links
on the link server and that we should use the (empty) link server
on the link server and that we should use the (empty) link server
index directory as a package index.
index directory as a package index.
The working set contains the distributions we retrieved.
The working set contains the distributions we retrieved.
>>> for dist in ws:
>>> for dist in ws:
... print_(dist)
... print_(dist)
demo 0.2
demo 0.2
demoneeded 1.1
demoneeded 1.1
We got demoneeded because it was a dependency of demo.
We got demoneeded because it was a dependency of demo.
And the actual eggs were added to the eggs directory.
And the actual eggs were added to the eggs directory.
>>> ls(dest)
>>> ls(dest)
d demo-0.2-py2.4.egg
d demo-0.2-py2.4.egg
d demoneeded-1.1-py2.4.egg
d demoneeded-1.1-py2.4.egg
If we remove the version restriction on demo, but specify a false
If we remove the version restriction on demo, but specify a false
value for newest, no new distributions will be installed:
value for newest, no new distributions will be installed:
>>> 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/',
... newest=False)
... newest=False)
>>> ls(dest)
>>> ls(dest)
d demo-0.2-py2.4.egg
d demo-0.2-py2.4.egg
d demoneeded-1.1-py2.4.egg
d demoneeded-1.1-py2.4.egg
If we leave off the newest 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/')
>>> ls(dest)
>>> ls(dest)
d demo-0.2-py2.4.egg
d demo-0.2-py2.4.egg
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
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 candidates for newer versions of both packages. By default,
release candidates for newer versions of both packages. By default,
final releases are preferred. 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)
True
True
The old setting is returned.
The old setting is returned.
>>> 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/')
>>> for dist in ws:
>>> for dist in ws:
... print_(dist)
... print_(dist)
demo 0.4c1
demo 0.4c1
demoneeded 1.2c1
demoneeded 1.2c1
>>> ls(dest)
>>> ls(dest)
d demo-0.2-py2.4.egg
d demo-0.2-py2.4.egg
d demo-0.3-py2.4.egg
d demo-0.3-py2.4.egg
d demo-0.4c1-py2.4.egg
d demo-0.4c1-py2.4.egg
d demoneeded-1.1-py2.4.egg
d demoneeded-1.1-py2.4.egg
d demoneeded-1.2c1-py2.4.egg
d demoneeded-1.2c1-py2.4.egg
Let's put the setting back to the default.
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 specific 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,
... links=[link_server], index=link_server+'index/')
... links=[link_server], index=link_server+'index/')
>>> for dist in ws:
>>> for dist in ws:
... print_(dist)
... print_(dist)
demo 0.3
demo 0.3
other 1.0
other 1.0
demoneeded 1.0
demoneeded 1.0
>>> ls(dest)
>>> ls(dest)
d demo-0.2-py2.4.egg
d demo-0.2-py2.4.egg
d demo-0.3-py2.4.egg
d demo-0.3-py2.4.egg
d demo-0.4c1-py2.4.egg
d demo-0.4c1-py2.4.egg
d demoneeded-1.0-py2.4.egg
d demoneeded-1.0-py2.4.egg
d demoneeded-1.1-py2.4.egg
d demoneeded-1.1-py2.4.egg
d demoneeded-1.2c1-py2.4.egg
d demoneeded-1.2c1-py2.4.egg
d other-1.0-py2.4.egg
d other-1.0-py2.4.egg
>>> rmdir(dest)
>>> rmdir(dest)
Specifying version information independent of requirements
Specifying version information independent of requirements
----------------------------------------------------------
----------------------------------------------------------
Sometimes it's useful to specify version information independent 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
to the install function, mapping project names to version numbers,
to the install function, mapping project names to version numbers,
then the versions numbers will be used.
then the versions numbers will be used.
>>> 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/',
... versions = dict(demo='0.2', demoneeded='1.0'))
... versions = dict(demo='0.2', demoneeded='1.0'))
>>> [d.version for d in ws]
>>> [d.version for d in ws]
['0.2', '1.0']
['0.2', '1.0']
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
dependencies 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:
>>> from zope.testing.loggingsupport import InstalledHandler
>>> from zope.testing.loggingsupport import InstalledHandler
>>> handler = InstalledHandler('zc.buildout.easy_install')
>>> handler = InstalledHandler('zc.buildout.easy_install')
>>> import logging
>>> import logging
>>> logging.getLogger('zc.buildout.easy_install').propagate = False
>>> logging.getLogger('zc.buildout.easy_install').propagate = False
>>> ws = zc.buildout.easy_install.install(
>>> ws = zc.buildout.easy_install.install(
... ['demo >0.2'], dest, links=[link_server],
... ['demo >0.2'], dest, links=[link_server],
... index=link_server+'index/',
... index=link_server+'index/',
... versions = dict(demo='0.2', demoneeded='1.0'))
... versions = dict(demo='0.2', demoneeded='1.0'))
Traceback (most recent call last):
Traceback (most recent call last):
...
...
IncompatibleVersionError: Bad version 0.2
IncompatibleVersionError: Bad version 0.2
>>> print_(handler)
>>> print_(handler)
zc.buildout.easy_install DEBUG
zc.buildout.easy_install DEBUG
Installing 'demo >0.2'.
Installing 'demo >0.2'.
zc.buildout.easy_install ERROR
zc.buildout.easy_install ERROR
The version, 0.2, is not consistent with the requirement, 'demo>0.2'.
The version, 0.2, is not consistent with the requirement, 'demo>0.2'.
>>> handler.clear()
>>> handler.clear()
If no versions are specified, a debugging message will be output
If no versions are specified, a debugging message will be output
reporting that a version was picked automatically:
reporting that a version was picked automatically:
>>> 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/',
... )
... )
>>> print_(handler) # doctest: +ELLIPSIS
>>> print_(handler) # doctest: +ELLIPSIS
zc.buildout.easy_install DEBUG
zc.buildout.easy_install DEBUG
Installing 'demo'.
Installing 'demo'.
zc.buildout.easy_install INFO
zc.buildout.easy_install INFO
Getting distribution for 'demo'.
Getting distribution for 'demo'.
zc.buildout.easy_install INFO
zc.buildout.easy_install INFO
Got demo 0.3.
Got demo 0.3.
zc.buildout.easy_install DEBUG
zc.buildout.easy_install DEBUG
Picked: demo = 0.3
Picked: demo = 0.3
zc.buildout.easy_install DEBUG
zc.buildout.easy_install DEBUG
Getting required 'demoneeded'
Getting required 'demoneeded'
zc.buildout.easy_install DEBUG
zc.buildout.easy_install DEBUG
required by demo 0.3.
required by demo 0.3.
zc.buildout.easy_install INFO
zc.buildout.easy_install INFO
Getting distribution for 'demoneeded'.
Getting distribution for 'demoneeded'.
zc.buildout.easy_install DEBUG
zc.buildout.easy_install DEBUG
Running easy_install:...
Running easy_install:...
zc.buildout.easy_install INFO
zc.buildout.easy_install INFO
Got demoneeded 1.1.
Got demoneeded 1.1.
zc.buildout.easy_install DEBUG
zc.buildout.easy_install DEBUG
Picked: demoneeded = 1.1
Picked: demoneeded = 1.1
zc.buildout.easy_install DEBUG
zc.buildout.easy_install DEBUG
Installing 'demo'.
Installing 'demo'.
zc.buildout.easy_install DEBUG
zc.buildout.easy_install DEBUG
We have the best distribution that satisfies 'demo'.
We have the best distribution that satisfies 'demo'.
zc.buildout.easy_install DEBUG
zc.buildout.easy_install DEBUG
Picked: demo = 0.3
Picked: demo = 0.3
zc.buildout.easy_install DEBUG
zc.buildout.easy_install DEBUG
Getting required 'demoneeded'
Getting required 'demoneeded'
zc.buildout.easy_install DEBUG
zc.buildout.easy_install DEBUG
required by demo 0.3.
required by demo 0.3.
zc.buildout.easy_install DEBUG
zc.buildout.easy_install DEBUG
We have the best distribution that satisfies 'demoneeded'.
We have the best distribution that satisfies 'demoneeded'.
zc.buildout.easy_install DEBUG
zc.buildout.easy_install DEBUG
Picked: demoneeded = 1.1
Picked: demoneeded = 1.1
>>> handler.uninstall()
>>> handler.uninstall()
>>> logging.getLogger('zc.buildout.easy_install').propagate = True
>>> logging.getLogger('zc.buildout.easy_install').propagate = True
We can request that we get an error if versions are picked:
We can request that we get an error if versions are picked:
>>> zc.buildout.easy_install.allow_picked_versions(False)
>>> zc.buildout.easy_install.allow_picked_versions(False)
True
True
(The old setting is returned.)
(The old setting is returned.)
>>> 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/',
... )
... )
Traceback (most recent call last):
Traceback (most recent call last):
...
...
UserError: Picked: demo = 0.3
UserError: Picked: demo = 0.3
>>> zc.buildout.easy_install.allow_picked_versions(True)
>>> zc.buildout.easy_install.allow_picked_versions(True)
False
False
The function default_versions can be used to get and set default
The function default_versions can be used to get and set default
version information to be used when no version information is passes.
version information to be used when no version information is passes.
If called with an argument, it sets the default versions:
If called with an argument, it sets the default versions:
>>> zc.buildout.easy_install.default_versions(dict(demoneeded='1'))
>>> zc.buildout.easy_install.default_versions(dict(demoneeded='1'))
... # doctest: +ELLIPSIS
... # doctest: +ELLIPSIS
{...}
{...}
It always returns the previous default versions. If called without an
It always returns the previous default versions. If called without an
argument, it simply returns the default versions without changing
argument, it simply returns the default versions without changing
them:
them:
>>> zc.buildout.easy_install.default_versions()
>>> zc.buildout.easy_install.default_versions()
{'demoneeded': '1'}
{'demoneeded': '1'}
So with the default versions set, we'll get the requested version even
So with the default versions set, we'll get the requested version even
if the versions option isn't used:
if the versions option isn't used:
>>> 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/',
... )
... )
>>> [d.version for d in ws]
>>> [d.version for d in ws]
['0.3', '1.0']
['0.3', '1.0']
Of course, we can unset the default versions by passing an empty
Of course, we can unset the default versions by passing an empty
dictionary:
dictionary:
>>> zc.buildout.easy_install.default_versions({})
>>> zc.buildout.easy_install.default_versions({})
{'demoneeded': '1'}
{'demoneeded': '1'}
>>> 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/',
... )
... )
>>> [d.version for d in ws]
>>> [d.version for d in ws]
['0.3', '1.1']
['0.3', '1.1']
Dependency links
Dependency links
----------------
----------------
Setuptools allows metadata that describes where to search for package
Setuptools allows metadata that describes where to search for package
dependencies. This option is called dependency_links. Buildout has its
dependencies. This option is called dependency_links. Buildout has its
own notion of where to look for dependencies, but it also uses the
own notion of where to look for dependencies, but it also uses the
setup tools dependency_links information if it's available.
setup tools dependency_links information if it's available.
Let's demo this by creating an egg that specifies dependency_links.
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
To begin, let's create a new egg repository. This repository hold a
newer version of the 'demoneeded' egg than the sample repository does.
newer version of the 'demoneeded' egg than the sample repository does.
>>> repoloc = tmpdir('repo')
>>> repoloc = tmpdir('repo')
>>> from zc.buildout.tests import create_egg
>>> from zc.buildout.tests import create_egg
>>> create_egg('demoneeded', '1.2', repoloc)
>>> create_egg('demoneeded', '1.2', repoloc)
>>> link_server2 = start_server(repoloc)
>>> link_server2 = start_server(repoloc)
Turn on logging on this server so that we can see when eggs are pulled
Turn on logging on this server so that we can see when eggs are pulled
from it.
from it.
>>> _ = get(link_server2 + 'enable_server_logging')
>>> _ = get(link_server2 + 'enable_server_logging')
GET 200 /enable_server_logging
GET 200 /enable_server_logging
Now we can create an egg that specifies that its dependencies are
Now we can create an egg that specifies that its dependencies are
found on this server.
found on this server.
>>> repoloc = tmpdir('repo2')
>>> repoloc = tmpdir('repo2')
>>> create_egg('hasdeps', '1.0', repoloc,
>>> create_egg('hasdeps', '1.0', repoloc,
... install_requires = "'demoneeded'",
... install_requires = "'demoneeded'",
... dependency_links = [link_server2])
... dependency_links = [link_server2])
Let's add the egg to another repository.
Let's add the egg to another repository.
>>> link_server3 = start_server(repoloc)
>>> link_server3 = start_server(repoloc)
Now let's install the egg.
Now let's install the egg.
>>> example_dest = tmpdir('example-install')
>>> example_dest = tmpdir('example-install')
>>> workingset = zc.buildout.easy_install.install(
>>> workingset = zc.buildout.easy_install.install(
... ['hasdeps'], example_dest,
... ['hasdeps'], example_dest,
... links=[link_server3], index=link_server3+'index/')
... links=[link_server3], index=link_server3+'index/')
GET 200 /
GET 200 /
GET 200 /demoneeded-1.2-pyN.N.egg
GET 200 /demoneeded-1.2-pyN.N.egg
The server logs show that the dependency was retrieved from the server
The server logs show that the dependency was retrieved from the server
specified in the dependency_links.
specified in the dependency_links.
Now let's see what happens if we provide two different ways to retrieve
Now let's see what happens if we provide two different ways to retrieve
the dependencies.
the dependencies.
>>> rmdir(example_dest)
>>> rmdir(example_dest)
>>> example_dest = tmpdir('example-install')
>>> example_dest = tmpdir('example-install')
>>> workingset = zc.buildout.easy_install.install(
>>> workingset = zc.buildout.easy_install.install(
... ['hasdeps'], example_dest, index=link_server+'index/',
... ['hasdeps'], example_dest, index=link_server+'index/',
... links=[link_server, link_server3])
... links=[link_server, link_server3])
GET 200 /
GET 200 /
GET 200 /demoneeded-1.2-pyN.N.egg
GET 200 /demoneeded-1.2-pyN.N.egg
Once again the dependency is fetched from the logging server even
Once again the dependency is fetched from the logging server even
though it is also available from the non-logging server. This is
though it is also available from the non-logging server. This is
because the version on the logging server is newer and buildout
because the version on the logging server is newer and buildout
normally chooses the newest egg available.
normally chooses the newest egg available.
If you wish to control where dependencies come from regardless of
If you wish to control where dependencies come from regardless of
dependency_links setup metadata use the 'use_dependency_links' option
dependency_links setup metadata use the 'use_dependency_links' option
to zc.buildout.easy_install.install().
to zc.buildout.easy_install.install().
>>> rmdir(example_dest)
>>> rmdir(example_dest)
>>> example_dest = tmpdir('example-install')
>>> example_dest = tmpdir('example-install')
>>> workingset = zc.buildout.easy_install.install(
>>> workingset = zc.buildout.easy_install.install(
... ['hasdeps'], example_dest, index=link_server+'index/',
... ['hasdeps'], example_dest, index=link_server+'index/',
... links=[link_server, link_server3],
... links=[link_server, link_server3],
... use_dependency_links=False)
... use_dependency_links=False)
Notice that this time the dependency egg is not fetched from the
Notice that this time the dependency egg is not fetched from the
logging server. When you specify not to use dependency_links, eggs
logging server. When you specify not to use dependency_links, eggs
will only be searched for using the links you explicitly provide.
will only be searched for using the links you explicitly provide.
Another way to control this option is with the
Another way to control this option is with the
zc.buildout.easy_install.use_dependency_links() function. This
zc.buildout.easy_install.use_dependency_links() function. This
function sets the default behavior for the zc.buildout.easy_install()
function sets the default behavior for the zc.buildout.easy_install()
function.
function.
>>> zc.buildout.easy_install.use_dependency_links(False)
>>> zc.buildout.easy_install.use_dependency_links(False)
True
True
The function returns its previous setting.
The function returns its previous setting.
>>> rmdir(example_dest)
>>> rmdir(example_dest)
>>> example_dest = tmpdir('example-install')
>>> example_dest = tmpdir('example-install')
>>> workingset = zc.buildout.easy_install.install(
>>> workingset = zc.buildout.easy_install.install(
... ['hasdeps'], example_dest, index=link_server+'index/',
... ['hasdeps'], example_dest, index=link_server+'index/',
... links=[link_server, link_server3])
... links=[link_server, link_server3])
It can be overridden by passing a keyword argument to the install
It can be overridden by passing a keyword argument to the install
function.
function.
>>> rmdir(example_dest)
>>> rmdir(example_dest)
>>> example_dest = tmpdir('example-install')
>>> example_dest = tmpdir('example-install')
>>> workingset = zc.buildout.easy_install.install(
>>> workingset = zc.buildout.easy_install.install(
... ['hasdeps'], example_dest, index=link_server+'index/',
... ['hasdeps'], example_dest, index=link_server+'index/',
... links=[link_server, link_server3],
... links=[link_server, link_server3],
... use_dependency_links=True)
... use_dependency_links=True)
GET 200 /demoneeded-1.2-pyN.N.egg
GET 200 /demoneeded-1.2-pyN.N.egg
To return the dependency_links behavior to normal call the function again.
To return the dependency_links behavior to normal call the function again.
>>> zc.buildout.easy_install.use_dependency_links(True)
>>> zc.buildout.easy_install.use_dependency_links(True)
False
False
>>> rmdir(example_dest)
>>> rmdir(example_dest)
>>> example_dest = tmpdir('example-install')
>>> example_dest = tmpdir('example-install')
>>> workingset = zc.buildout.easy_install.install(
>>> workingset = zc.buildout.easy_install.install(
... ['hasdeps'], example_dest, index=link_server+'index/',
... ['hasdeps'], example_dest, index=link_server+'index/',
... links=[link_server, link_server3])
... links=[link_server, link_server3])
GET 200 /demoneeded-1.2-pyN.N.egg
GET 200 /demoneeded-1.2-pyN.N.egg
Script generation
Script generation
-----------------
-----------------
The easy_install module provides support for creating scripts from
The easy_install module provides support for creating scripts from
eggs. It provides a function similar to setuptools except that it
eggs. It provides a function similar to setuptools except that it
provides facilities for baking a script's path into the script. This
provides facilities for baking a script's path into the script. This
has two advantages:
has two advantages:
- The eggs to be used by a script are not chosen at run time, making
- The eggs to be used by a script are not chosen at run time, making
startup faster and, more importantly, deterministic.
startup faster and, more importantly, deterministic.
- The script doesn't have to import pkg_resources because the logic
- The script doesn't have to import pkg_resources because the logic
that pkg_resources would execute at run time is executed at
that pkg_resources would execute at run time is executed at
script-creation time.
script-creation time.
The scripts method can be used to generate scripts. Let's create a
The scripts method can be used to generate scripts. Let's create a
destination directory for it to place them in:
destination directory for it to place them in:
>>> import tempfile
>>> import tempfile
>>> bin = tmpdir('bin')
>>> bin = tmpdir('bin')
Now, we'll use the scripts method to generate scripts in this directory
Now, we'll use the scripts method to generate scripts in this directory
from the demo egg:
from the demo egg:
>>> import sys
>>> import sys
>>> scripts = zc.buildout.easy_install.scripts(
>>> scripts = zc.buildout.easy_install.scripts(
... ['demo'], ws, sys.executable, bin)
... ['demo'], ws, sys.executable, bin)
the three arguments we passed were:
the three arguments we passed were:
1. A sequence of distribution requirements. These are of the same
1. A sequence of distribution requirements. These are of the same
form as setuptools requirements. Here we passed a single
form as setuptools requirements. Here we passed a single
requirement, for the version 0.1 demo distribution.
requirement, for the version 0.1 demo distribution.
2. A working set,
2. A working set,
3. The destination directory.
3. The destination directory.
The bin directory now contains a generated script:
The bin directory now contains a generated script:
>>> ls(bin)
>>> ls(bin)
- demo
- demo
The return value is a list of the scripts generated:
The return value is a list of the scripts generated:
>>> import os, sys
>>> import os, sys
>>> if sys.platform == 'win32':
>>> if sys.platform == 'win32':
... scripts == [os.path.join(bin, 'demo.exe'),
... scripts == [os.path.join(bin, 'demo.exe'),
... os.path.join(bin, 'demo-script.py')]
... os.path.join(bin, 'demo-script.py')]
... else:
... else:
... scripts == [os.path.join(bin, 'demo')]
... scripts == [os.path.join(bin, 'demo')]
True
True
Note that in Windows, 2 files are generated for each script. A script
Note that in Windows, 2 files are generated for each script. A script
file, ending in '-script.py', and an exe file that allows the script
file, ending in '-script.py', and an exe file that allows the script
to be invoked directly without having to specify the Python
to be invoked directly without having to specify the Python
interpreter and without having to provide a '.py' suffix.
interpreter and without having to provide a '.py' suffix.
The demo script run the entry point defined in the demo egg:
The demo script run the entry point defined in the demo egg:
>>> cat(bin, 'demo') # doctest: +NORMALIZE_WHITESPACE
>>> cat(bin, 'demo') # doctest: +NORMALIZE_WHITESPACE
#!/usr/local/bin/python2.7
#!/usr/local/bin/python2.7
<BLANKLINE>
<BLANKLINE>
import sys
import sys
sys.path[0:0] = [
sys.path[0:0] = [
'/sample-install/demo-0.3-py2.4.egg',
'/sample-install/demo-0.3-py2.4.egg',
'/sample-install/demoneeded-1.1-py2.4.egg',
'/sample-install/demoneeded-1.1-py2.4.egg',
]
]
<BLANKLINE>
<BLANKLINE>
import eggrecipedemo
import eggrecipedemo
<BLANKLINE>
<BLANKLINE>
if __name__ == '__main__':
if __name__ == '__main__':
sys.exit(eggrecipedemo.main())
sys.exit(eggrecipedemo.main())
Some things to note:
Some things to note:
- The demo and demoneeded eggs are added to the beginning of sys.path.
- The demo and demoneeded eggs are added to the beginning of sys.path.
- The module for the script entry point is imported and the entry
- The module for the script entry point is imported and the entry
point, in this case, 'main', is run.
point, in this case, 'main', is run.
Rather than requirement strings, you can pass tuples containing 3
Rather than requirement strings, you can pass tuples containing 3
strings:
strings:
- A script name,
- A script name,
- A module,
- A module,
- 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 entry 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(
... [('demo', 'eggrecipedemo', 'main')], ws,
... [('demo', 'eggrecipedemo', 'main')], ws,
... sys.executable, bin)
... sys.executable, bin)
>>> cat(bin, 'demo') # doctest: +NORMALIZE_WHITESPACE
>>> cat(bin, 'demo') # doctest: +NORMALIZE_WHITESPACE
#!/usr/local/bin/python2.7
#!/usr/local/bin/python2.7
<BLANKLINE>
<BLANKLINE>
import sys
import sys
sys.path[0:0] = [
sys.path[0:0] = [
'/sample-install/demo-0.3-py2.4.egg',
'/sample-install/demo-0.3-py2.4.egg',
'/sample-install/demoneeded-1.1-py2.4.egg',
'/sample-install/demoneeded-1.1-py2.4.egg',
]
]
<BLANKLINE>
<BLANKLINE>
import eggrecipedemo
import eggrecipedemo
<BLANKLINE>
<BLANKLINE>
if __name__ == '__main__':
if __name__ == '__main__':
sys.exit(eggrecipedemo.main())
sys.exit(eggrecipedemo.main())
Passing entry-point information directly is handy when using eggs (or
Passing entry-point information directly is handy when using eggs (or
distributions) that don't declare their entry points, such as
distributions) that don't declare their entry points, such as
distributions that aren't based on setuptools.
distributions that aren't based on setuptools.
The interpreter keyword argument can be used to generate a script that can
The interpreter keyword argument can be used to generate a script that can
be used to invoke the Python interactive interpreter with the path set
be used to invoke the Python interactive interpreter with the path set
based on the working set. This generated script can also be used to
based on the working set. This generated script can also be used to
run other scripts with the path set on the working set:
run other scripts with the path set on the working set:
>>> scripts = zc.buildout.easy_install.scripts(
>>> scripts = zc.buildout.easy_install.scripts(
... ['demo'], ws, sys.executable, bin, interpreter='py')
... ['demo'], ws, sys.executable, bin, interpreter='py')
>>> ls(bin)
>>> ls(bin)
- demo
- demo
- py
- py
>>> if sys.platform == 'win32':
>>> if sys.platform == 'win32':
... scripts == [os.path.join(bin, 'demo.exe'),
... scripts == [os.path.join(bin, 'demo.exe'),
... os.path.join(bin, 'demo-script.py'),
... os.path.join(bin, 'demo-script.py'),
... os.path.join(bin, 'py.exe'),
... os.path.join(bin, 'py.exe'),
... os.path.join(bin, 'py-script.py')]
... os.path.join(bin, 'py-script.py')]
... else:
... else:
... scripts == [os.path.join(bin, 'demo'),
... scripts == [os.path.join(bin, 'demo'),
... os.path.join(bin, 'py')]
... os.path.join(bin, 'py')]
True
True
The py script simply runs the Python interactive interpreter with
The py script simply runs the Python interactive interpreter with
the path set:
the path set:
>>> cat(bin, 'py') # doctest: +NORMALIZE_WHITESPACE
>>> cat(bin, 'py') # doctest: +NORMALIZE_WHITESPACE
#!/usr/local/bin/python2.7
#!/usr/local/bin/python2.7
<BLANKLINE>
<BLANKLINE>
import sys
import sys
<BLANKLINE>
<BLANKLINE>
sys.path[0:0] = [
sys.path[0:0] = [
'/sample-install/demo-0.3-pyN.N.egg',
'/sample-install/demo-0.3-pyN.N.egg',
'/sample-install/demoneeded-1.1-pyN.N.egg',
'/sample-install/demoneeded-1.1-pyN.N.egg',
]
]
<BLANKLINE>
<BLANKLINE>
_interactive = True
_interactive = True
if len(sys.argv) > 1:
if len(sys.argv) > 1:
_options, _args = __import__("getopt").getopt(sys.argv[1:], 'ic:m:')
_options, _args = __import__("getopt").getopt(sys.argv[1:], 'ic:m:')
_interactive = False
_interactive = False
for (_opt, _val) in _options:
for (_opt, _val) in _options:
if _opt == '-i':
if _opt == '-i':
_interactive = True
_interactive = True
elif _opt == '-c':
elif _opt == '-c':
exec(_val)
exec(_val)
elif _opt == '-m':
elif _opt == '-m':
sys.argv[1:] = _args
sys.argv[1:] = _args
_args = []
_args = []
__import__("runpy").run_module(
__import__("runpy").run_module(
_val, {}, "__main__", alter_sys=True)
_val, {}, "__main__", alter_sys=True)
<BLANKLINE>
<BLANKLINE>
if _args:
if _args:
sys.argv[:] = _args
sys.argv[:] = _args
__file__ = _args[0]
__file__ = _args[0]
del _options, _args
del _options, _args
__file__f = open(__file__)
__file__f = open(__file__)
exec(compile(__file__f.read(), __file__, "exec"))
exec(compile(__file__f.read(), __file__, "exec"))
__file__f.close(); del __file__f
__file__f.close(); del __file__f
<BLANKLINE>
<BLANKLINE>
if _interactive:
if _interactive:
del _interactive
del _interactive
__import__("code").interact(banner="", local=globals())
__import__("code").interact(banner="", local=globals())
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.
>>> write('ascript', r'''
>>> write('ascript', r'''
... "demo doc"
... "demo doc"
... import sys
... import sys
... print_ = lambda *a: sys.stdout.write(' '.join(map(str, a))+'\n')
... print_ = lambda *a: sys.stdout.write(' '.join(map(str, a))+'\n')
... print_(sys.argv)
... print_(sys.argv)
... print_((__name__, __file__, __doc__))
... print_((__name__, __file__, __doc__))
... ''')
... ''')
>>> print_(system(join(bin, 'py')+' ascript a b c'), end='')
>>> print_(system(join(bin, 'py')+' ascript a b c'), end='')
['ascript', 'a', 'b', 'c']
['ascript', 'a', 'b', 'c']
('__main__', 'ascript', 'demo doc')
('__main__', 'ascript', 'demo doc')
For Python 2.5 and higher, you can also use the -m option to run a
For Python 2.5 and higher, you can also use the -m option to run a
module:
module:
>>> if sys.version_info < (2, 5):
>>> if sys.version_info < (2, 5):
... print ('usage: pdb.py blah blah blah')
... print ('usage: pdb.py blah blah blah')
... else:
... else:
... print_(system(join(bin, 'py')+' -m pdb'), end='')
... print_(system(join(bin, 'py')+' -m pdb'), end='')
... # doctest: +ELLIPSIS
... # doctest: +ELLIPSIS
usage: pdb.py ...
usage: pdb.py ...
>>> print_(system(join(bin, 'py')+' -m pdb what'), end='')
>>> print_(system(join(bin, 'py')+' -m pdb what'), end='')
Error: what does not exist
Error: what does not exist
An additional argument 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.
>>> bin = tmpdir('bin2')
>>> bin = tmpdir('bin2')
>>> scripts = zc.buildout.easy_install.scripts(
>>> scripts = zc.buildout.easy_install.scripts(
... ['demo'], ws, sys.executable, bin, dict(demo='run'))
... ['demo'], ws, sys.executable, bin, dict(demo='run'))
>>> if sys.platform == 'win32':
>>> if sys.platform == 'win32':
... scripts == [os.path.join(bin, 'run.exe'),
... scripts == [os.path.join(bin, 'run.exe'),
... os.path.join(bin, 'run-script.py')]
... os.path.join(bin, 'run-script.py')]
... else:
... else:
... scripts == [os.path.join(bin, 'run')]
... scripts == [os.path.join(bin, 'run')]
True
True
>>> ls(bin)
>>> ls(bin)
- run
- run
>>> print_(system(os.path.join(bin, 'run')), end='')
>>> print_(system(os.path.join(bin, 'run')), end='')
3 1
3 1
Including extra paths in scripts
Including extra paths in scripts
--------------------------------
--------------------------------
We can pass a keyword argument, extra paths, to cause 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:
>>> foo = tmpdir('foo')
>>> foo = tmpdir('foo')
>>> scripts = zc.buildout.easy_install.scripts(
>>> scripts = zc.buildout.easy_install.scripts(
... ['demo'], ws, sys.executable, bin, dict(demo='run'),
... ['demo'], ws, sys.executable, bin, dict(demo='run'),
... extra_paths=[foo])
... extra_paths=[foo])
>>> cat(bin, 'run') # doctest: +NORMALIZE_WHITESPACE
>>> cat(bin, 'run') # doctest: +NORMALIZE_WHITESPACE
#!/usr/local/bin/python2.7
#!/usr/local/bin/python2.7
<BLANKLINE>
<BLANKLINE>
import sys
import sys
sys.path[0:0] = [
sys.path[0:0] = [
'/sample-install/demo-0.3-py2.4.egg',
'/sample-install/demo-0.3-py2.4.egg',
'/sample-install/demoneeded-1.1-py2.4.egg',
'/sample-install/demoneeded-1.1-py2.4.egg',
'/foo',
'/foo',
]
]
<BLANKLINE>
<BLANKLINE>
import eggrecipedemo
import eggrecipedemo
<BLANKLINE>
<BLANKLINE>
if __name__ == '__main__':
if __name__ == '__main__':
sys.exit(eggrecipedemo.main())
sys.exit(eggrecipedemo.main())
Providing script arguments
Providing script arguments
--------------------------
--------------------------
An "argument" keyword argument can be used to pass arguments to an
An "argument" keyword argument can be used to pass arguments to an
entry point. The value passed is a source string to be placed between the
entry point. The value passed is a source string to be placed between the
parentheses in the call:
parentheses in the call:
>>> scripts = zc.buildout.easy_install.scripts(
>>> scripts = zc.buildout.easy_install.scripts(
... ['demo'], ws, sys.executable, bin, dict(demo='run'),
... ['demo'], ws, sys.executable, bin, dict(demo='run'),
... arguments='1, 2')
... arguments='1, 2')
>>> cat(bin, 'run') # doctest: +NORMALIZE_WHITESPACE
>>> cat(bin, 'run') # doctest: +NORMALIZE_WHITESPACE
#!/usr/local/bin/python2.7
#!/usr/local/bin/python2.7
import sys
import sys
sys.path[0:0] = [
sys.path[0:0] = [
'/sample-install/demo-0.3-py2.4.egg',
'/sample-install/demo-0.3-py2.4.egg',
'/sample-install/demoneeded-1.1-py2.4.egg',
'/sample-install/demoneeded-1.1-py2.4.egg',
]
]
<BLANKLINE>
<BLANKLINE>
import eggrecipedemo
import eggrecipedemo
<BLANKLINE>
<BLANKLINE>
if __name__ == '__main__':
if __name__ == '__main__':
sys.exit(eggrecipedemo.main(1, 2))
sys.exit(eggrecipedemo.main(1, 2))
Passing initialization code
Passing initialization code
---------------------------
---------------------------
You can also pass script initialization code:
You can also pass script initialization code:
>>> scripts = zc.buildout.easy_install.scripts(
>>> scripts = zc.buildout.easy_install.scripts(
... ['demo'], ws, sys.executable, bin, dict(demo='run'),
... ['demo'], ws, sys.executable, bin, dict(demo='run'),
... arguments='1, 2',
... arguments='1, 2',
... initialization='import os\nos.chdir("foo")')
... initialization='import os\nos.chdir("foo")')
>>> cat(bin, 'run') # doctest: +NORMALIZE_WHITESPACE
>>> cat(bin, 'run') # doctest: +NORMALIZE_WHITESPACE
#!/usr/local/bin/python2.7
#!/usr/local/bin/python2.7
import sys
import sys
sys.path[0:0] = [
sys.path[0:0] = [
'/sample-install/demo-0.3-py2.4.egg',
'/sample-install/demo-0.3-py2.4.egg',
'/sample-install/demoneeded-1.1-py2.4.egg',
'/sample-install/demoneeded-1.1-py2.4.egg',
]
]
<BLANKLINE>
<BLANKLINE>
import os
import os
os.chdir("foo")
os.chdir("foo")
<BLANKLINE>
<BLANKLINE>
import eggrecipedemo
import eggrecipedemo
<BLANKLINE>
<BLANKLINE>
if __name__ == '__main__':
if __name__ == '__main__':
sys.exit(eggrecipedemo.main(1, 2))
sys.exit(eggrecipedemo.main(1, 2))
Relative paths
Relative paths
--------------
--------------
Sometimes, you want to be able to move a buildout directory around and
Sometimes, you want to be able to move a buildout directory around and
have scripts still work without having to rebuild them. We can
have scripts still work without having to rebuild them. We can
control this using the relative_paths option to install. You need
control this using the relative_paths option to install. You need
to pass a common base directory of the scripts and eggs:
to pass a common base directory of the scripts and eggs:
>>> bo = tmpdir('bo')
>>> bo = tmpdir('bo')
>>> ba = tmpdir('ba')
>>> ba = tmpdir('ba')
>>> mkdir(bo, 'eggs')
>>> mkdir(bo, 'eggs')
>>> mkdir(bo, 'bin')
>>> mkdir(bo, 'bin')
>>> mkdir(bo, 'other')
>>> mkdir(bo, 'other')
>>> ws = zc.buildout.easy_install.install(
>>> ws = zc.buildout.easy_install.install(
... ['demo'], join(bo, 'eggs'), links=[link_server],
... ['demo'], join(bo, 'eggs'), links=[link_server],
... index=link_server+'index/')
... index=link_server+'index/')
>>> scripts = zc.buildout.easy_install.scripts(
>>> scripts = zc.buildout.easy_install.scripts(
... ['demo'], ws, sys.executable, join(bo, 'bin'), dict(demo='run'),
... ['demo'], ws, sys.executable, join(bo, 'bin'), dict(demo='run'),
... extra_paths=[ba, join(bo, 'bar')],
... extra_paths=[ba, join(bo, 'bar')],
... interpreter='py',
... interpreter='py',
... relative_paths=bo)
... relative_paths=bo)
>>> cat(bo, 'bin', 'run')
>>> cat(bo, 'bin', 'run')
#!/usr/local/bin/python2.7
#!/usr/local/bin/python2.7
<BLANKLINE>
<BLANKLINE>
import os
import os
<BLANKLINE>
<BLANKLINE>
join = os.path.join
join = os.path.join
base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
base = os.path.dirname(base)
base = os.path.dirname(base)
<BLANKLINE>
<BLANKLINE>
import sys
import sys
sys.path[0:0] = [
sys.path[0:0] = [
join(base, 'eggs/demo-0.3-pyN.N.egg'),
join(base, 'eggs/demo-0.3-pyN.N.egg'),
join(base, 'eggs/demoneeded-1.1-pyN.N.egg'),
join(base, 'eggs/demoneeded-1.1-pyN.N.egg'),
'/ba',
'/ba',
join(base, 'bar'),
join(base, 'bar'),
]
]
<BLANKLINE>
<BLANKLINE>
import eggrecipedemo
import eggrecipedemo
<BLANKLINE>
<BLANKLINE>
if __name__ == '__main__':
if __name__ == '__main__':
sys.exit(eggrecipedemo.main())
sys.exit(eggrecipedemo.main())
Note that the extra path we specified that was outside the directory
Note that the extra path we specified that was outside the directory
passed as relative_paths wasn't converted to a relative path.
passed as relative_paths wasn't converted to a relative path.
Of course, running the script works:
Of course, running the script works:
>>> print_(system(join(bo, 'bin', 'run')), end='')
>>> print_(system(join(bo, 'bin', 'run')), end='')
3 1
3 1
We specified an interpreter and its paths are adjusted too:
We specified an interpreter and its paths are adjusted too:
>>> cat(bo, 'bin', 'py')
>>> cat(bo, 'bin', 'py')
#!/usr/local/bin/python2.7
#!/usr/local/bin/python2.7
<BLANKLINE>
<BLANKLINE>
import os
import os
<BLANKLINE>
<BLANKLINE>
join = os.path.join
join = os.path.join
base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
base = os.path.dirname(base)
base = os.path.dirname(base)
<BLANKLINE>
<BLANKLINE>
import sys
import sys
<BLANKLINE>
<BLANKLINE>
sys.path[0:0] = [
sys.path[0:0] = [
join(base, 'eggs/demo-0.3-pyN.N.egg'),
join(base, 'eggs/demo-0.3-pyN.N.egg'),
join(base, 'eggs/demoneeded-1.1-pyN.N.egg'),
join(base, 'eggs/demoneeded-1.1-pyN.N.egg'),
'/ba',
'/ba',
join(base, 'bar'),
join(base, 'bar'),
]
]
<BLANKLINE>
<BLANKLINE>
_interactive = True
_interactive = True
if len(sys.argv) > 1:
if len(sys.argv) > 1:
_options, _args = __import__("getopt").getopt(sys.argv[1:], 'ic:m:')
_options, _args = __import__("getopt").getopt(sys.argv[1:], 'ic:m:')
_interactive = False
_interactive = False
for (_opt, _val) in _options:
for (_opt, _val) in _options:
if _opt == '-i':
if _opt == '-i':
_interactive = True
_interactive = True
elif _opt == '-c':
elif _opt == '-c':
exec(_val)
exec(_val)
elif _opt == '-m':
elif _opt == '-m':
sys.argv[1:] = _args
sys.argv[1:] = _args
_args = []
_args = []
__import__("runpy").run_module(
__import__("runpy").run_module(
_val, {}, "__main__", alter_sys=True)
_val, {}, "__main__", alter_sys=True)
<BLANKLINE>
<BLANKLINE>
if _args:
if _args:
sys.argv[:] = _args
sys.argv[:] = _args
__file__ = _args[0]
__file__ = _args[0]
del _options, _args
del _options, _args
__file__f = open(__file__)
__file__f = open(__file__)
exec(compile(__file__f.read(), __file__, "exec"))
exec(compile(__file__f.read(), __file__, "exec"))
__file__f.close(); del __file__f
__file__f.close(); del __file__f
<BLANKLINE>
<BLANKLINE>
if _interactive:
if _interactive:
del _interactive
del _interactive
__import__("code").interact(banner="", local=globals())
__import__("code").interact(banner="", local=globals())
Installing distutils-style scripts
Installing distutils-style scripts
----------------------------------
----------------------------------
Most python libraries use the console_scripts entry point nowadays. But
Most python libraries use the console_scripts entry point nowadays. But
several still have a ``scripts=['bin/something']`` in their setup() call.
several still have a ``scripts=['bin/something']`` in their setup() call.
Buildout also installs those:
Buildout also installs those:
>>> distdir = tmpdir('distutilsscriptdir')
>>> distdir = tmpdir('distutilsscriptdir')
>>> distbin = tmpdir('distutilsscriptbin')
>>> distbin = tmpdir('distutilsscriptbin')
>>> ws = zc.buildout.easy_install.install(
>>> ws = zc.buildout.easy_install.install(
... ['other'], distdir,
... ['other'], distdir,
... links=[link_server], index=link_server+'index/')
... links=[link_server], index=link_server+'index/')
>>> scripts = zc.buildout.easy_install.scripts(
>>> scripts = zc.buildout.easy_install.scripts(
... ['other'], ws, sys.executable, distbin)
... ['other'], ws, sys.executable, distbin)
>>> ls(distbin)
>>> ls(distbin)
- distutilsscript
- distutilsscript
It also works for zipped eggs:
It also works for zipped eggs:
>>> distdir2 = tmpdir('distutilsscriptdir2')
>>> distdir2 = tmpdir('distutilsscriptdir2')
>>> distbin2 = tmpdir('distutilsscriptbin2')
>>> distbin2 = tmpdir('distutilsscriptbin2')
>>> ws = zc.buildout.easy_install.install(
>>> ws = zc.buildout.easy_install.install(
... ['du_zipped'], distdir2,
... ['du_zipped'], distdir2,
... links=[link_server], index=link_server+'index/')
... links=[link_server], index=link_server+'index/')
>>> scripts = zc.buildout.easy_install.scripts(
>>> scripts = zc.buildout.easy_install.scripts(
... ['du_zipped'], ws, sys.executable, distbin2)
... ['du_zipped'], ws, sys.executable, distbin2)
>>> ls(distbin2)
>>> ls(distbin2)
- distutilsscript
- distutilsscript
Distutils copies the script files verbatim, apart from a line at the top that
Distutils copies the script files verbatim, apart from a line at the top that
looks like ``#!/usr/bin/python``, which gets replaced by the actual python
looks like ``#!/usr/bin/python``, which gets replaced by the actual python
interpreter. Buildout does the same, but additionally also adds the sys.path
interpreter. Buildout does the same, but additionally also adds the sys.path
like for the console_scripts:
like for the console_scripts:
>>> cat(distbin, 'distutilsscript')
>>> cat(distbin, 'distutilsscript')
#!/usr/local/bin/python2.7
#!/usr/local/bin/python2.7
<BLANKLINE>
<BLANKLINE>
import sys
import sys
sys.path[0:0] = [
sys.path[0:0] = [
'/distutilsscriptdir/other-1.0-pyN.N.egg',
'/distutilsscriptdir/other-1.0-pyN.N.egg',
]
]
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
import sys; sys.stdout.write("distutils!\n")
import sys; sys.stdout.write("distutils!\n")
Due to the nature of distutils scripts, buildout cannot pass arguments as
Due to the nature of distutils scripts, buildout cannot pass arguments as
there's no specific method to pass them to.
there's no specific method to pass them to.
Handling custom build options for extensions provided in source distributions
Handling custom build options for extensions provided in source distributions
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
Sometimes, we need to control how extension modules are built. The
Sometimes, we need to control how extension modules are built. The
build function provides this level of control. It takes a single
build function provides this level of control. It takes a single
package specification, downloads a source distribution, and builds it
package specification, downloads a source distribution, and builds it
with specified custom build options.
with specified custom build options.
The build function takes 3 positional arguments:
The build function takes 3 positional arguments:
spec
spec
A package specification for a source distribution
A package specification for a source distribution
dest
dest
A destination directory
A destination directory
build_ext
build_ext
A dictionary of options to be passed to the distutils build_ext
A dictionary of options to be passed to the distutils build_ext
command when building extensions.
command when building extensions.
It supports a number of optional keyword arguments:
links
a sequence of URLs, file names, or directories to look for
links to distributions,
index
The URL of an index server, or almost any other valid URL. :)
If not specified, the Python Package Index,
http://pypi.python.org/simple/, is used. You can specify an
alternate index with this option. If you use the links option and
if the links point to the needed distributions, then the index can
be anything and will be largely ignored. In the examples, here,
we'll just point to an empty directory on our link server. This
will make our examples run a little bit faster.
path
A list of additional directories to search for locally-installed
distributions.
newest
A boolean value indicating whether to search for new distributions
when already-installed distributions meet the requirement. When
this is true, the default, and when the destination directory is
not None, then the install function will search for the newest
distributions that satisfy the requirements.
versions
A dictionary mapping project names to version numbers to be used
when selecting distributions. This can be used to specify a set of
distribution versions independent of other requirements.
Our link server included a source distribution that includes a simple
extension, extdemo.c::
#include <Python.h>
#include <extdemo.h>
static PyMethodDef methods[] = {};
PyMODINIT_FUNC
initextdemo(void)
{
PyObject *m;
m = Py_InitModule3("extdemo", methods, "");
#ifdef TWO
PyModule_AddObject(m, "val", PyInt_FromLong(2));
#else
PyModule_AddObject(m, "val", PyInt_FromLong(EXTDEMO));
#endif
}
The extension depends on a system-dependent include file, extdemo.h,
that defines a constant, EXTDEMO, that is exposed by the extension.
We'll add an include directory to our sample buildout and add the
needed include file to it:
>>> mkdir('include')
>>> write('include', 'extdemo.h',
... """
... #define EXTDEMO 42
... """)
Now, we can use the build function to create an egg from the source
distribution:
>>> zc.buildout.easy_install.build(
... 'extdemo', dest,
... {'include-dirs': os.path.join(sample_buildout, 'include')},
... links=[link_server], index=link_server+'index/')
['/sample-install/extdemo-1.4-py2.4-unix-i686.egg']
The function returns the list of eggs
Now if we look in our destination directory, we see we have an extdemo egg:
>>> ls(dest)
d demo-0.2-py2.4.egg
d demo-0.3-py2.4.egg
d demoneeded-1.0-py2.4.egg
d demoneeded-1.1-py2.4.egg
d extdemo-1.4-py2.4-unix-i686.egg
Let's update our link server with a new version of extdemo:
>>> update_extdemo()
>>> print_(get(link_server), end='')
<html><body>
<a href="bigdemo-0.1-py2.4.egg">bigdemo-0.1-py2.4.egg</a><br>
<a href="demo-0.1-py2.4.egg">demo-0.1-py2.4.egg</a><br>
<a href="demo-0.2-py2.4.egg">demo-0.2-py2.4.egg</a><br>
<a href="demo-0.3-py2.4.egg">demo-0.3-py2.4.egg</a><br>
<a href="demo-0.4c1-py2.4.egg">demo-0.4c1-py2.4.egg</a><br>
<a href="demoneeded-1.0.zip">demoneeded-1.0.zip</a><br>
<a href="demoneeded-1.1.zip">demoneeded-1.1.zip</a><br>
<a href="demoneeded-1.2c1.zip">demoneeded-1.2c1.zip</a><br>
<a href="du_zipped-1.0-pyN.N.egg">du_zipped-1.0-pyN.N.egg</a><br>
<a href="extdemo-1.4.zip">extdemo-1.4.zip</a><br>
<a href="extdemo-1.5.zip">extdemo-1.5.zip</a><br>
<a href="index/">index/</a><br>
<a href="other-1.0-py2.4.egg">other-1.0-py2.4.egg</a><br>
</body></html>
The easy_install caches information about servers to reduce network
access. To see the update, we have to call the clear_index_cache
function to clear the index cache:
>>> zc.buildout.easy_install.clear_index_cache()
If we run build with newest set to False, we won't get an update:
>>> zc.buildout.easy_install.build(
... 'extdemo', dest,
... {'include-dirs': os.path.join(sample_buildout, 'include')},
... links=[link_server], index=link_server+'index/',
... newest=False)
['/sample-install/extdemo-1.4-py2.4-linux-i686.egg']
>>> ls(dest)
d demo-0.2-py2.4.egg
d demo-0.3-py2.4.egg
d demoneeded-1.0-py2.4.egg
d demoneeded-1.1-py2.4.egg
d extdemo-1.4-py2.4-unix-i686.egg
But if we run it with the default True setting for newest, then we'll
get an updated egg:
>>> zc.buildout.easy_install.build(
... 'extdemo', dest,
... {'include-dirs': os.path.join(sample_buildout, 'include')},
... links=[link_server], index=link_server+'index/')
['/sample-install/extdemo-1.5-py2.4-unix-i686.egg']
>>> ls(dest)
d demo-0.2-py2.4.egg
d demo-0.3-py2.4.egg
d demoneeded-1.0-py2.4.egg
d demoneeded-1.1-py2.4.egg
d extdemo-1.4-py2.4-unix-i686.egg
d extdemo-1.5-py2.4-unix-i686.egg
The versions option also influences the versions used. For example,
if we specify a version for extdemo, then that will be used, even
though it isn't the newest. Let's clean out the destination directory
first:
>>> import os
>>> for name in os.listdir(dest):
... remove(dest, name)
>>> zc.buildout.easy_install.build(
... 'extdemo', dest,
... {'include-dirs': os.path.join(sample_buildout, 'include')},
... links=[link_server], index=link_server+'index/',
... versions=dict(extdemo='1.4'))
['/sample-install/extdemo-1.4-py2.4-unix-i686.egg']
>>> ls(dest)
d extdemo-1.4-py2.4-unix-i686.egg
Handling custom build options for extensions in develop eggs
------------------------------------------------------------
The develop function is similar to the build function, except that,
rather than building an egg from a source directory containing a
setup.py script.
The develop function takes 2 positional arguments:
setup
The path to a setup script, typically named "setup.py", or a
directory containing a setup.py script.
dest
The directory to install the egg link to
It supports some optional keyword argument:
build_ext
A dictionary of options to be passed to the distutils build_ext
command when building extensions.
We have a local directory containing the extdemo source:
>>> ls(extdemo)
- MANIFEST
- MANIFEST.in
- README
- extdemo.c
- setup.py
Now, we can use the develop function to create a develop egg from the source
distribution:
>>> zc.buildout.easy_install.develop(
... extdemo, dest,
... {'include-dirs': os.path.join(sample_buildout, 'include')})
'/sample-install/extdemo.egg-link'
The name of the egg link created is returned.
It supports a number of optional keyword arguments:
links
a sequence of URLs, file names, or directories to look for
links to distributions,
index
The URL of an index server, or almost any other valid URL. :)
If not specified, the Python Package Index,
http://pypi.python.org/simple/, is used. You can specify an
alternate index with this option. If you use the links option and
if the links point to the needed distributions, then the index can
be anything and will be largely ignored. In the examples, here,
we'll just point to an empty directory on our link server. This
will make our examples run a little bit faster.
path
A list of additional directories to search for locally-installed
distributions.
newest
A boolean value indicating whether to search for new distributions
when already-installed distributions meet the requirement. When
this is true, the default, and when the destination directory is
not None, then the install function will search for the newest
distributions that satisfy the requirements.
versions
A dictionary mapping project names to version numbers to be used
when selecting distributions. This can be used to specify a set of
distribution versions independent of other requirements.
Our link server included a source distribution that includes a simple
extension, extdemo.c::
#include <Python.h>
#include <extdemo.h>
static PyMethodDef methods[] = {};
PyMODINIT_FUNC
initextdemo(void)
{
PyObject *m;
m = Py_InitModule3("extdemo", methods, "");
#ifdef TWO
PyModule_AddObject(m, "val", PyInt_FromLong(2));
#else
PyModule_AddObject(m, "val", PyInt_FromLong(EXTDEMO));
#endif
}
The extension depends on a system-dependent include file, extdemo.h,
that defines a constant, EXTDEMO, that is exposed by the extension.
We'll add an include directory to our sample buildout and add the
needed include file to it:
>>> mkdir('include')
>>> write('include', 'extdemo.h',
... """
... #define EXTDEMO 42
... """)
Now, we can use the build function to create an egg from the source
distribution:
>>> zc.buildout.easy_install.build(
... 'extdemo', dest,
... {'include-dirs': os.path.join(sample_buildout, 'include')},
... links=[link_server], index=link_server+'index/')
['/sample-install/extdemo-1.4-py2.4-unix-i686.egg']
The function returns the list of eggs
Now if we look in our destination directory, we see we have an extdemo egg:
>>> ls(dest)
d demo-0.2-py2.4.egg
d demo-0.3-py2.4.egg
d demoneeded-1.0-py2.4.egg
d demoneeded-1.1-py2.4.egg
d extdemo-1.4-py2.4-unix-i686.egg
Let's update our link server with a new version of extdemo:
>>> update_extdemo()
>>> print_(get(link_server), end='')
<html><body>
<a href="bigdemo-0.1-py2.4.egg">bigdemo-0.1-py2.4.egg</a><br>
<a href="demo-0.1-py2.4.egg">demo-0.1-py2.4.egg</a><br>
<a href="demo-0.2-py2.4.egg">demo-0.2-py2.4.egg</a><br>
<a href="demo-0.3-py2.4.egg">demo-0.3-py2.4.egg</a><br>
<a href="demo-0.4c1-py2.4.egg">demo-0.4c1-py2.4.egg</a><br>
<a href="demoneeded-1.0.zip">demoneeded-1.0.zip</a><br>
<a href="demoneeded-1.1.zip">demoneeded-1.1.zip</a><br>
<a href="demoneeded-1.2c1.zip">demoneeded-1.2c1.zip</a><br>
<a href="du_zipped-1.0-pyN.N.egg">du_zipped-1.0-pyN.N.egg</a><br>
<a href="extdemo-1.4.zip">extdemo-1.4.zip</a><br>
<a href="extdemo-1.5.zip">extdemo-1.5.zip</a><br>
<a href="index/">index/</a><br>
<a href="other-1.0-py2.4.egg">other-1.0-py2.4.egg</a><br>
</body></html>
The easy_install caches information about servers to reduce network
access. To see the update, we have to call the clear_index_cache
function to clear the index cache:
>>> zc.buildout.easy_install.clear_index_cache()
If we run build with newest set to False, we won't get an update:
>>> zc.buildout.easy_install.build(
... 'extdemo', dest,
... {'include-dirs': os.path.join(sample_buildout, 'include')},
... links=[link_server], index=link_server+'index/',
... newest=False)
['/sample-install/extdemo-1.4-py2.4-linux-i686.egg']
>>> ls(dest)
d demo-0.2-py2.4.egg
d demo-0.3-py2.4.egg
d demoneeded-1.0-py2.4.egg
d demoneeded-1.1-py2.4.egg
d extdemo-1.4-py2.4-unix-i686.egg
But if we run it with the default True setting for newest, then we'll
get an updated egg:
>>> zc.buildout.easy_install.build(
... 'extdemo', dest,
... {'include-dirs': os.path.join(sample_buildout, 'include')},
... links=[link_server], index=link_server+'index/')
['/sample-install/extdemo-1.5-py2.4-unix-i686.egg']
>>> ls(dest)
d demo-0.2-py2.4.egg
d demo-0.3-py2.4.egg
d demoneeded-1.0-py2.4.egg
d demoneeded-1.1-py2.4.egg
d extdemo-1.4-py2.4-unix-i686.egg
d extdemo-1.5-py2.4-unix-i686.egg
The versions option also influences the versions used. For example,
if we specify a version for extdemo, then that will be used, even
though it isn't the newest. Let's clean out the destination directory
first:
>>> import os
>>> for name in os.listdir(dest):
... remove(dest, name)
>>> zc.buildout.easy_install.build(
... 'extdemo', dest,
... {'include-dirs': os.path.join(sample_buildout, 'include')},
... links=[link_server], index=link_server+'index/',
... versions=dict(extdemo='1.4'))
['/sample-install/extdemo-1.4-py2.4-unix-i686.egg']
>>> ls(dest)
d extdemo-1.4-py2.4-unix-i686.egg
Handling custom build options for extensions in develop eggs
------------------------------------------------------------
The develop function is similar to the build function, except that,
rather than building an egg from a source directory containing a
setup.py script.
The develop function takes 2 positional arguments:
setup
The path to a setup script, typically named "setup.py", or a
directory containing a setup.py script.
dest
The directory to install the egg link to
It supports some optional keyword argument:
build_ext
A dictionary of options to be passed to the distutils build_ext
command when building extensions.
We have a local directory containing the extdemo source:
>>> ls(extdemo)
- MANIFEST
- MANIFEST.in
- README
- extdemo.c
- setup.py
Now, we can use the develop function to create a develop egg from the source
distribution:
>>> zc.buildout.easy_install.develop(
... extdemo, dest,
... {'include-dirs': os.path.join(sample_buildout, 'include')})
'/sample-install/extdemo.egg-link'
The name of the egg link created is returned.
Now if we look in our destination directory, we see we have an extdemo
Now if we look in our destination directory, we see we have an extdemo
egg link:
egg link:
>>> ls(dest)
>>> ls(dest)
d extdemo-1.4-py2.4-unix-i686.egg
d extdemo-1.4-py2.4-unix-i686.egg
- extdemo.egg-link
- extdemo.egg-link
And that the source directory contains the compiled extension:
And that the source directory contains the compiled extension:
>>> contents = os.listdir(extdemo)
>>> contents = os.listdir(extdemo)
>>> bool([f for f in contents if f.endswith('.so') or f.endswith('.pyd')])
>>> bool([f for f in contents if f.endswith('.so') or f.endswith('.pyd')])
True
True
Download cache
Download cache
--------------
--------------
Normally, when distributions are installed, if any processing is
Normally, when distributions are installed, if any processing is
needed, they are downloaded from the internet to a temporary directory
needed, they are downloaded from the internet to a temporary directory
and then installed from there. A download cache can be used to avoid
and then installed from there. A download cache can be used to avoid
the download step. This can be useful to reduce network access and to
the download step. This can be useful to reduce network access and to
create source distributions of an entire buildout.
create source distributions of an entire buildout.
A download cache is specified by calling the download_cache
A download cache is specified by calling the download_cache
function. The function always returns the previous setting. If no
function. The function always returns the previous setting. If no
argument is passed, then the setting is unchanged. If an argument is
argument is passed, then the setting is unchanged. If an argument is
passed, the download cache is set to the given path, which must point
passed, the download cache is set to the given path, which must point
to an existing directory. Passing None clears the cache setting.
to an existing directory. Passing None clears the cache setting.
To see this work, we'll create a directory and set it as the cache
To see this work, we'll create a directory and set it as the cache
directory:
directory:
>>> cache = tmpdir('cache')
>>> cache = tmpdir('cache')
>>> zc.buildout.easy_install.download_cache(cache)
>>> zc.buildout.easy_install.download_cache(cache)
We'll recreate our destination directory:
We'll recreate our destination directory:
>>> remove(dest)
>>> remove(dest)
>>> dest = tmpdir('sample-install')
>>> dest = tmpdir('sample-install')
We'd like to see what is being fetched from the server, so we'll
We'd like to see what is being fetched from the server, so we'll
enable server logging:
enable server logging:
>>> _ = get(link_server+'enable_server_logging')
>>> _ = get(link_server+'enable_server_logging')
GET 200 /enable_server_logging
GET 200 /enable_server_logging
Now, if we install demo, and extdemo:
Now, if we install demo, and extdemo:
>>> ws = zc.buildout.easy_install.install(
>>> ws = zc.buildout.easy_install.install(
... ['demo==0.2'], dest,
... ['demo==0.2'], dest,
... links=[link_server], index=link_server+'index/')
... links=[link_server], index=link_server+'index/')
GET 200 /
GET 200 /
GET 404 /index/demo/
GET 404 /index/demo/
GET 200 /index/
GET 200 /index/
GET 200 /demo-0.2-py2.4.egg
GET 200 /demo-0.2-py2.4.egg
GET 404 /index/demoneeded/
GET 404 /index/demoneeded/
GET 200 /demoneeded-1.1.zip
GET 200 /demoneeded-1.1.zip
>>> zc.buildout.easy_install.build(
>>> zc.buildout.easy_install.build(
... 'extdemo', dest,
... 'extdemo', dest,
... {'include-dirs': os.path.join(sample_buildout, 'include')},
... {'include-dirs': os.path.join(sample_buildout, 'include')},
... links=[link_server], index=link_server+'index/')
... links=[link_server], index=link_server+'index/')
GET 404 /index/extdemo/
GET 404 /index/extdemo/
GET 200 /extdemo-1.5.zip
GET 200 /extdemo-1.5.zip
['/sample-install/extdemo-1.5-py2.4-linux-i686.egg']
['/sample-install/extdemo-1.5-py2.4-linux-i686.egg']
Not only will we get eggs in our destination directory:
Not only will we get eggs in our destination directory:
>>> ls(dest)
>>> ls(dest)
d demo-0.2-py2.4.egg
d demo-0.2-py2.4.egg
d demoneeded-1.1-py2.4.egg
d demoneeded-1.1-py2.4.egg
d extdemo-1.5-py2.4-linux-i686.egg
d extdemo-1.5-py2.4-linux-i686.egg
But we'll get distributions in the cache directory:
But we'll get distributions in the cache directory:
>>> ls(cache)
>>> ls(cache)
- demo-0.2-py2.4.egg
- demo-0.2-py2.4.egg
- demoneeded-1.1.zip
- demoneeded-1.1.zip
- extdemo-1.5.zip
- extdemo-1.5.zip
The cache directory contains uninstalled distributions, such as zipped
The cache directory contains uninstalled distributions, such as zipped
eggs or source distributions.
eggs or source distributions.
Let's recreate our destination directory and clear the index cache:
Let's recreate our destination directory and clear the index cache:
>>> remove(dest)
>>> remove(dest)
>>> dest = tmpdir('sample-install')
>>> dest = tmpdir('sample-install')
>>> zc.buildout.easy_install.clear_index_cache()
>>> zc.buildout.easy_install.clear_index_cache()
Now when we install the distributions:
Now when we install the distributions:
>>> ws = zc.buildout.easy_install.install(
>>> ws = zc.buildout.easy_install.install(
... ['demo==0.2'], dest,
... ['demo==0.2'], dest,
... links=[link_server], index=link_server+'index/')
... links=[link_server], index=link_server+'index/')
GET 200 /
GET 200 /
GET 404 /index/demo/
GET 404 /index/demo/
GET 200 /index/
GET 200 /index/
GET 404 /index/demoneeded/
GET 404 /index/demoneeded/
>>> zc.buildout.easy_install.build(
>>> zc.buildout.easy_install.build(
... 'extdemo', dest,
... 'extdemo', dest,
... {'include-dirs': os.path.join(sample_buildout, 'include')},
... {'include-dirs': os.path.join(sample_buildout, 'include')},
... links=[link_server], index=link_server+'index/')
... links=[link_server], index=link_server+'index/')
GET 404 /index/extdemo/
GET 404 /index/extdemo/
['/sample-install/extdemo-1.5-py2.4-linux-i686.egg']
['/sample-install/extdemo-1.5-py2.4-linux-i686.egg']
>>> ls(dest)
>>> ls(dest)
d demo-0.2-py2.4.egg
d demo-0.2-py2.4.egg
d demoneeded-1.1-py2.4.egg
d demoneeded-1.1-py2.4.egg
d extdemo-1.5-py2.4-linux-i686.egg
d extdemo-1.5-py2.4-linux-i686.egg
Note that we didn't download the distributions from the link server.
Note that we didn't download the distributions from the link server.
If we remove the restriction on demo, we'll download a newer version
If we remove the restriction on demo, we'll download a newer version
from the link server:
from the link server:
>>> ws = zc.buildout.easy_install.install(
>>> ws = zc.buildout.easy_install.install(
... ['demo'], dest,
... ['demo'], dest,
... links=[link_server], index=link_server+'index/')
... links=[link_server], index=link_server+'index/')
GET 200 /demo-0.3-py2.4.egg
GET 200 /demo-0.3-py2.4.egg
Normally, the download cache is the preferred 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
---------------------------------------
---------------------------------------
A download cache can be used as the basis of application source
A download cache can be used as the basis of application source
releases. In an application source release, we want to distribute an
releases. In an application source release, we want to distribute an
application that can be built without making any network accesses. In
application that can be built without making any network accesses. In
this case, we distribute a download cache and tell the easy_install
this case, we distribute a download cache and tell the easy_install
module to install from the download cache only, without making network
module to install from the download cache only, without making network
accesses. The install_from_cache function can be used to signal that
accesses. The install_from_cache function can be used to signal that
packages should be installed only from the download cache. The
packages should be installed only from the download cache. The
function always returns the previous setting. Calling it with no
function always returns the previous setting. Calling it with no
arguments returns the current setting without changing it:
arguments returns the current setting without changing it:
>>> zc.buildout.easy_install.install_from_cache()
>>> zc.buildout.easy_install.install_from_cache()
False
False
Calling it with a boolean value changes the setting and returns the
Calling it with a boolean value changes the setting and returns the
previous setting:
previous setting:
>>> zc.buildout.easy_install.install_from_cache(True)
>>> zc.buildout.easy_install.install_from_cache(True)
False
False
Let's remove demo-0.3-py2.4.egg from the cache, clear the index cache,
Let's remove demo-0.3-py2.4.egg from the cache, clear the index cache,
recreate the destination directory, and reinstall demo:
recreate the destination directory, and reinstall demo:
>>> for f in os.listdir(cache):
>>> for f in os.listdir(cache):
... if f.startswith('demo-0.3-'):
... if f.startswith('demo-0.3-'):
... remove(cache, f)
... remove(cache, f)
>>> zc.buildout.easy_install.clear_index_cache()
>>> zc.buildout.easy_install.clear_index_cache()
>>> remove(dest)
>>> remove(dest)
>>> dest = tmpdir('sample-install')
>>> dest = tmpdir('sample-install')
>>> ws = zc.buildout.easy_install.install(
>>> ws = zc.buildout.easy_install.install(
... ['demo'], dest,
... ['demo'], dest,
... links=[link_server], index=link_server+'index/')
... links=[link_server], index=link_server+'index/')
>>> ls(dest)
>>> ls(dest)
d demo-0.2-py2.4.egg
d demo-0.2-py2.4.egg
d demoneeded-1.1-py2.4.egg
d demoneeded-1.1-py2.4.egg
This time, we didn't download from or even query the link server.
This time, we didn't download from or even query the link server.
.. Disable the download cache:
.. Disable the download cache:
>>> zc.buildout.easy_install.download_cache(None)
>>> zc.buildout.easy_install.download_cache(None)
'/cache'
'/cache'
>>> zc.buildout.easy_install.install_from_cache(False)
>>> zc.buildout.easy_install.install_from_cache(False)
True
True
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment