~gary/zc.buildout/python-support-10

« back to all changes in this revision

Viewing changes to src/zc/buildout/tests.py

  • Committer: Gary Poster
  • Date: 2010-03-19 14:24:54 UTC
  • mfrom: (535.1.26 gary-8)
  • Revision ID: gary.poster@canonical.com-20100319142454-p5currsgehvuy28i
merge buildout trunk with system python support branches.

Show diffs side-by-side

added added

removed removed

Lines of Context:
53
53
 
54
54
    >>> ls('develop-eggs')
55
55
    -  foo.egg-link
 
56
    -  z3c.recipe.scripts.egg-link
56
57
    -  zc.recipe.egg.egg-link
57
58
 
58
59
    """
84
85
 
85
86
    >>> ls('develop-eggs')
86
87
    -  foo.egg-link
 
88
    -  z3c.recipe.scripts.egg-link
87
89
    -  zc.recipe.egg.egg-link
88
90
 
89
91
    >>> print system(join('bin', 'buildout')+' -vvv'), # doctest: +ELLIPSIS
383
385
    Error: Couldn't find a distribution for 'demoneeded'.
384
386
    """
385
387
 
 
388
def show_eggs_from_site_packages():
 
389
    """
 
390
Sometimes you want to know what eggs are coming from site-packages.  This
 
391
might be for a diagnostic, or so that you can get a starting value for the
 
392
allowed-eggs-from-site-packages option.  The -v flag will also include this
 
393
information.
 
394
 
 
395
Our "py_path" has the "demoneeded," "demo"
 
396
packages available.  We'll ask for "bigdemo," which will get both of them.
 
397
 
 
398
Here's our set up.
 
399
 
 
400
    >>> py_path, site_packages_path = make_py()
 
401
    >>> create_sample_sys_install(site_packages_path)
 
402
 
 
403
    >>> write('buildout.cfg',
 
404
    ... '''
 
405
    ... [buildout]
 
406
    ... parts = eggs
 
407
    ... prefer-final = true
 
408
    ... find-links = %(link_server)s
 
409
    ...
 
410
    ... [primed_python]
 
411
    ... executable = %(py_path)s
 
412
    ...
 
413
    ... [eggs]
 
414
    ... recipe = zc.recipe.egg:eggs
 
415
    ... python = primed_python
 
416
    ... eggs = bigdemo
 
417
    ... ''' % globals())
 
418
 
 
419
Now here is the output.  The lines that begin with "Egg from site-packages:"
 
420
indicate the eggs from site-packages that have been selected.  You'll see
 
421
we have two: demo 0.3 and demoneeded 1.1.
 
422
 
 
423
    >>> print system(buildout+" -v")
 
424
    Installing 'zc.buildout', 'setuptools'.
 
425
    We have a develop egg: zc.buildout V
 
426
    We have the best distribution that satisfies 'setuptools'.
 
427
    Picked: setuptools = V
 
428
    Installing 'zc.recipe.egg'.
 
429
    We have a develop egg: zc.recipe.egg V
 
430
    Installing eggs.
 
431
    Installing 'bigdemo'.
 
432
    We have no distributions for bigdemo that satisfies 'bigdemo'.
 
433
    Getting distribution for 'bigdemo'.
 
434
    Got bigdemo 0.1.
 
435
    Picked: bigdemo = 0.1
 
436
    Getting required 'demo'
 
437
      required by bigdemo 0.1.
 
438
    We have a develop egg: demo V
 
439
    Egg from site-packages: demo 0.3
 
440
    Getting required 'demoneeded'
 
441
      required by demo 0.3.
 
442
    We have a develop egg: demoneeded V
 
443
    Egg from site-packages: demoneeded 1.1
 
444
    <BLANKLINE>
 
445
    """
386
446
 
387
447
def test_comparing_saved_options_with_funny_characters():
388
448
    """
668
728
 
669
729
    >>> ls('develop-eggs')
670
730
    -  foox.egg-link
 
731
    -  z3c.recipe.scripts.egg-link
671
732
    -  zc.recipe.egg.egg-link
672
733
 
673
734
Create another:
692
753
    >>> ls('develop-eggs')
693
754
    -  foox.egg-link
694
755
    -  fooy.egg-link
 
756
    -  z3c.recipe.scripts.egg-link
695
757
    -  zc.recipe.egg.egg-link
696
758
 
697
759
Remove one:
709
771
 
710
772
    >>> ls('develop-eggs')
711
773
    -  fooy.egg-link
 
774
    -  z3c.recipe.scripts.egg-link
712
775
    -  zc.recipe.egg.egg-link
713
776
 
714
777
Remove the other:
723
786
All gone
724
787
 
725
788
    >>> ls('develop-eggs')
 
789
    -  z3c.recipe.scripts.egg-link
726
790
    -  zc.recipe.egg.egg-link
727
791
    '''
728
792
 
797
861
    ...            + join(sample_buildout, 'eggs'))
798
862
 
799
863
    >>> ls('develop-eggs')
 
864
    -  z3c.recipe.scripts.egg-link
800
865
    -  zc.recipe.egg.egg-link
801
866
 
802
867
    >>> ls('eggs') # doctest: +ELLIPSIS
1769
1834
    1 2
1770
1835
    """
1771
1836
 
 
1837
def versions_section_ignored_for_dependency_in_favor_of_site_packages():
 
1838
    r"""
 
1839
This is a test for a bugfix.
 
1840
 
 
1841
The error showed itself when at least two dependencies were in a shared
 
1842
location like site-packages, and the first one met the "versions" setting.  The
 
1843
first dependency would be added, but subsequent dependencies from the same
 
1844
location (e.g., site-packages) would use the version of the package found in
 
1845
the shared location, ignoring the version setting.
 
1846
 
 
1847
We begin with a Python that has demoneeded version 1.1 installed and a
 
1848
demo version 0.3, all in a site-packages-like shared directory.  We need
 
1849
to create this.  ``eggrecipedemo.main()`` shows the number after the dot
 
1850
(that is, ``X`` in ``1.X``), for the demo package and the demoneeded
 
1851
package, so this demonstrates that our Python does in fact have demo
 
1852
version 0.3 and demoneeded version 1.1.
 
1853
 
 
1854
    >>> py_path = make_py_with_system_install(make_py, sample_eggs)
 
1855
    >>> print call_py(
 
1856
    ...     py_path,
 
1857
    ...     "import tellmy.version; print tellmy.version.__version__"),
 
1858
    1.1
 
1859
 
 
1860
Now here's a setup that would expose the bug, using the
 
1861
zc.buildout.easy_install API.
 
1862
 
 
1863
    >>> example_dest = tmpdir('example_dest')
 
1864
    >>> workingset = zc.buildout.easy_install.install(
 
1865
    ...     ['tellmy.version'], example_dest, links=[sample_eggs],
 
1866
    ...     executable=py_path,
 
1867
    ...     index=None,
 
1868
    ...     versions={'tellmy.version': '1.0'})
 
1869
    >>> for dist in workingset:
 
1870
    ...     res = str(dist)
 
1871
    ...     if res.startswith('tellmy.version'):
 
1872
    ...         print res
 
1873
    ...         break
 
1874
    tellmy.version 1.0
 
1875
 
 
1876
Before the bugfix, the desired tellmy.version distribution would have
 
1877
been blocked the one in site-packages.
 
1878
"""
 
1879
 
 
1880
def handle_namespace_package_in_both_site_packages_and_buildout_eggs():
 
1881
    r"""
 
1882
If you have the same namespace package in both site-packages and in
 
1883
buildout, we need to be very careful that faux-Python-executables and
 
1884
scripts generated by easy_install.sitepackage_safe_scripts correctly
 
1885
combine the two. We show this with the local recipe that uses the
 
1886
function, z3c.recipe.scripts.
 
1887
 
 
1888
To demonstrate this, we will create three packages: tellmy.version 1.0,
 
1889
tellmy.version 1.1, and tellmy.fortune 1.0.  tellmy.version 1.1 is installed.
 
1890
 
 
1891
    >>> py_path = make_py_with_system_install(make_py, sample_eggs)
 
1892
    >>> print call_py(
 
1893
    ...     py_path,
 
1894
    ...     "import tellmy.version; print tellmy.version.__version__")
 
1895
    1.1
 
1896
    <BLANKLINE>
 
1897
 
 
1898
Now we will create a buildout that creates a script and a faux-Python script.
 
1899
We want to see that both can successfully import the specified versions of
 
1900
tellmy.version and tellmy.fortune.
 
1901
 
 
1902
    >>> write('buildout.cfg',
 
1903
    ... '''
 
1904
    ... [buildout]
 
1905
    ... parts = eggs
 
1906
    ... find-links = %(link_server)s
 
1907
    ...
 
1908
    ... [primed_python]
 
1909
    ... executable = %(py_path)s
 
1910
    ...
 
1911
    ... [eggs]
 
1912
    ... recipe = z3c.recipe.scripts
 
1913
    ... python = primed_python
 
1914
    ... interpreter = py
 
1915
    ... include-site-packages = true
 
1916
    ... eggs = tellmy.version == 1.0
 
1917
    ...        tellmy.fortune == 1.0
 
1918
    ...        demo
 
1919
    ... script-initialization =
 
1920
    ...     import tellmy.version
 
1921
    ...     print tellmy.version.__version__
 
1922
    ...     import tellmy.fortune
 
1923
    ...     print tellmy.fortune.__version__
 
1924
    ... ''' % globals())
 
1925
 
 
1926
    >>> print system(buildout)
 
1927
    Installing eggs.
 
1928
    Getting distribution for 'tellmy.version==1.0'.
 
1929
    Got tellmy.version 1.0.
 
1930
    Getting distribution for 'tellmy.fortune==1.0'.
 
1931
    Got tellmy.fortune 1.0.
 
1932
    Getting distribution for 'demo'.
 
1933
    Got demo 0.4c1.
 
1934
    Getting distribution for 'demoneeded'.
 
1935
    Got demoneeded 1.2c1.
 
1936
    Generated script '/sample-buildout/bin/demo'.
 
1937
    Generated interpreter '/sample-buildout/bin/py'.
 
1938
    <BLANKLINE>
 
1939
 
 
1940
Finally, we are ready to see if it worked.  Prior to the bug fix that
 
1941
this tests, the results of both calls below was the following::
 
1942
 
 
1943
    1.1
 
1944
    Traceback (most recent call last):
 
1945
      ...
 
1946
    ImportError: No module named fortune
 
1947
    <BLANKLINE>
 
1948
 
 
1949
In other words, we got the site-packages version of tellmy.version, and
 
1950
we could not import tellmy.fortune at all.  The following are the correct
 
1951
results for the interpreter and for the script.
 
1952
 
 
1953
    >>> print call_py(
 
1954
    ...     join('bin', 'py'),
 
1955
    ...     "import tellmy.version; " +
 
1956
    ...     "print tellmy.version.__version__; " +
 
1957
    ...     "import tellmy.fortune; " +
 
1958
    ...     "print tellmy.fortune.__version__") # doctest: +ELLIPSIS
 
1959
    1.0
 
1960
    1.0...
 
1961
 
 
1962
    >>> print system(join('bin', 'demo'))
 
1963
    1.0
 
1964
    1.0
 
1965
    4 2
 
1966
    <BLANKLINE>
 
1967
    """
 
1968
 
 
1969
def handle_sys_path_version_hack():
 
1970
    r"""
 
1971
This is a test for a bugfix.
 
1972
 
 
1973
If you use a Python that has a different version of one of your
 
1974
dependencies, and the new package tries to do sys.path tricks in the
 
1975
setup.py to get a __version__, and it uses namespace packages, the older
 
1976
package will be loaded first, making the setup version the wrong number.
 
1977
While very arguably packages simply shouldn't do this, some do, and we
 
1978
don't want buildout to fall over when they do.
 
1979
 
 
1980
To demonstrate this, we will need to create a distribution that has one of
 
1981
these unpleasant tricks, and a Python that has an older version installed.
 
1982
 
 
1983
    >>> py_path, site_packages_path = make_py()
 
1984
    >>> for version in ('1.0', '1.1'):
 
1985
    ...     tmp = tempfile.mkdtemp()
 
1986
    ...     try:
 
1987
    ...         write(tmp, 'README.txt', '')
 
1988
    ...         mkdir(tmp, 'src')
 
1989
    ...         mkdir(tmp, 'src', 'tellmy')
 
1990
    ...         write(tmp, 'src', 'tellmy', '__init__.py',
 
1991
    ...             "__import__("
 
1992
    ...             "'pkg_resources').declare_namespace(__name__)\n")
 
1993
    ...         mkdir(tmp, 'src', 'tellmy', 'version')
 
1994
    ...         write(tmp, 'src', 'tellmy', 'version',
 
1995
    ...               '__init__.py', '__version__=%r\n' % version)
 
1996
    ...         write(
 
1997
    ...             tmp, 'setup.py',
 
1998
    ...             "from setuptools import setup\n"
 
1999
    ...             "import sys\n"
 
2000
    ...             "sys.path.insert(0, 'src')\n"
 
2001
    ...             "from tellmy.version import __version__\n"
 
2002
    ...             "setup(\n"
 
2003
    ...             " name='tellmy.version',\n"
 
2004
    ...             " package_dir = {'': 'src'},\n"
 
2005
    ...             " packages = ['tellmy', 'tellmy.version'],\n"
 
2006
    ...             " install_requires = ['setuptools'],\n"
 
2007
    ...             " namespace_packages=['tellmy'],\n"
 
2008
    ...             " zip_safe=True, version=__version__,\n"
 
2009
    ...             " author='bob', url='bob', author_email='bob')\n"
 
2010
    ...             )
 
2011
    ...         zc.buildout.testing.sdist(tmp, sample_eggs)
 
2012
    ...         if version == '1.0':
 
2013
    ...             # We install the 1.0 version in site packages the way a
 
2014
    ...             # system packaging system (debs, rpms) would do it.
 
2015
    ...             zc.buildout.testing.sys_install(tmp, site_packages_path)
 
2016
    ...     finally:
 
2017
    ...         shutil.rmtree(tmp)
 
2018
    >>> print call_py(
 
2019
    ...     py_path,
 
2020
    ...     "import tellmy.version; print tellmy.version.__version__")
 
2021
    1.0
 
2022
    <BLANKLINE>
 
2023
    >>> write('buildout.cfg',
 
2024
    ... '''
 
2025
    ... [buildout]
 
2026
    ... parts = eggs
 
2027
    ... find-links = %(sample_eggs)s
 
2028
    ...
 
2029
    ... [primed_python]
 
2030
    ... executable = %(py_path)s
 
2031
    ...
 
2032
    ... [eggs]
 
2033
    ... recipe = zc.recipe.egg:eggs
 
2034
    ... python = primed_python
 
2035
    ... eggs = tellmy.version == 1.1
 
2036
    ... ''' % globals())
 
2037
 
 
2038
Before the bugfix, running this buildout would generate this error:
 
2039
 
 
2040
    Installing eggs.
 
2041
    Getting distribution for 'tellmy.version==1.1'.
 
2042
    Installing tellmy.version 1.1
 
2043
    Caused installation of a distribution:
 
2044
    tellmy.version 1.0
 
2045
    with a different version.
 
2046
    Got None.
 
2047
    While:
 
2048
      Installing eggs.
 
2049
    Error: There is a version conflict.
 
2050
    We already have: tellmy.version 1.0
 
2051
    <BLANKLINE>
 
2052
 
 
2053
You can see the copiously commented fix for this in easy_install.py (see
 
2054
zc.buildout.easy_install.Installer._call_easy_install and particularly
 
2055
the comment leading up to zc.buildout.easy_install._easy_install_cmd).
 
2056
Now the install works correctly, as seen here.
 
2057
 
 
2058
    >>> print system(buildout)
 
2059
    Installing eggs.
 
2060
    Getting distribution for 'tellmy.version==1.1'.
 
2061
    Got tellmy.version 1.1.
 
2062
    <BLANKLINE>
 
2063
 
 
2064
    """
 
2065
 
 
2066
def isolated_include_site_packages():
 
2067
    """
 
2068
 
 
2069
This is an isolated test of the include_site_packages functionality, passing
 
2070
the argument directly to install, overriding a default.
 
2071
 
 
2072
Our "py_path" has the "demoneeded" and "demo" packages available.  We'll
 
2073
simply be asking for "demoneeded" here.
 
2074
 
 
2075
    >>> py_path, site_packages_path = make_py()
 
2076
    >>> create_sample_sys_install(site_packages_path)
 
2077
    >>> zc.buildout.easy_install.include_site_packages(False)
 
2078
    True
 
2079
 
 
2080
    >>> example_dest = tmpdir('site-packages-example-install')
 
2081
    >>> workingset = zc.buildout.easy_install.install(
 
2082
    ...     ['demoneeded'], example_dest, links=[], executable=py_path,
 
2083
    ...     index=None, include_site_packages=True)
 
2084
    >>> [dist.project_name for dist in workingset]
 
2085
    ['demoneeded']
 
2086
 
 
2087
That worked fine.  Let's try again with site packages not allowed (and
 
2088
reversing the default).
 
2089
 
 
2090
    >>> zc.buildout.easy_install.include_site_packages(True)
 
2091
    False
 
2092
 
 
2093
    >>> zc.buildout.easy_install.clear_index_cache()
 
2094
    >>> rmdir(example_dest)
 
2095
    >>> example_dest = tmpdir('site-packages-example-install')
 
2096
    >>> workingset = zc.buildout.easy_install.install(
 
2097
    ...     ['demoneeded'], example_dest, links=[], executable=py_path,
 
2098
    ...     index=None, include_site_packages=False)
 
2099
    Traceback (most recent call last):
 
2100
        ...
 
2101
    MissingDistribution: Couldn't find a distribution for 'demoneeded'.
 
2102
 
 
2103
That's a failure, as expected.
 
2104
 
 
2105
Now we explore an important edge case.
 
2106
 
 
2107
Some system Pythons include setuptools (and other Python packages) in their
 
2108
site-packages (or equivalent) using a .egg-info directory.  The pkg_resources
 
2109
module (from setuptools) considers a package installed using .egg-info to be a
 
2110
develop egg.
 
2111
 
 
2112
zc.buildout.buildout.Buildout.bootstrap will make setuptools and zc.buildout
 
2113
available to the buildout via the eggs directory, for normal eggs; or the
 
2114
develop-eggs directory, for develop-eggs.
 
2115
 
 
2116
If setuptools or zc.buildout is found in site-packages and considered by
 
2117
pkg_resources to be a develop egg, then the bootstrap code will use a .egg-link
 
2118
in the local develop-eggs, pointing to site-packages, in its entirety.  Because
 
2119
develop-eggs must always be available for searching for distributions, this
 
2120
indirectly brings site-packages back into the search path for distributions.
 
2121
 
 
2122
Because of this, we have to take special care that we still exclude
 
2123
site-packages even in this case.  See the comments about site packages in the
 
2124
Installer._satisfied and Installer._obtain methods for the implementation
 
2125
(as of this writing).
 
2126
 
 
2127
In this demonstration, we insert a link to the "demoneeded" distribution
 
2128
in our develop-eggs, which would bring the package back in, except for
 
2129
the special care we have taken to exclude it.
 
2130
 
 
2131
    >>> zc.buildout.easy_install.clear_index_cache()
 
2132
    >>> rmdir(example_dest)
 
2133
    >>> example_dest = tmpdir('site-packages-example-install')
 
2134
    >>> mkdir(example_dest, 'develop-eggs')
 
2135
    >>> write(example_dest, 'develop-eggs', 'demoneeded.egg-link',
 
2136
    ...       site_packages_path)
 
2137
    >>> workingset = zc.buildout.easy_install.install(
 
2138
    ...     ['demoneeded'], example_dest, links=[],
 
2139
    ...     path=[join(example_dest, 'develop-eggs')],
 
2140
    ...     executable=py_path,
 
2141
    ...     index=None, include_site_packages=False)
 
2142
    Traceback (most recent call last):
 
2143
        ...
 
2144
    MissingDistribution: Couldn't find a distribution for 'demoneeded'.
 
2145
 
 
2146
The MissingDistribution error shows that buildout correctly excluded the
 
2147
"site-packages" source even though it was indirectly included in the path
 
2148
via a .egg-link file.
 
2149
 
 
2150
    """
 
2151
 
 
2152
def allowed_eggs_from_site_packages():
 
2153
    """
 
2154
Sometimes you need or want to control what eggs from site-packages are used.
 
2155
The allowed-eggs-from-site-packages option allows you to specify a whitelist of
 
2156
project names that may be included from site-packages.  You can use globs to
 
2157
specify the value.  It defaults to a single value of '*', indicating that any
 
2158
package may come from site-packages.
 
2159
 
 
2160
This option interacts with include-site-packages in the following ways.
 
2161
 
 
2162
If include-site-packages is true, then allowed-eggs-from-site-packages filters
 
2163
what eggs from site-packages may be chosen.  If allowed-eggs-from-site-packages
 
2164
is an empty list, then no eggs from site-packages are chosen, but site-packages
 
2165
will still be included at the end of path lists.
 
2166
 
 
2167
If include-site-packages is false, allowed-eggs-from-site-packages is
 
2168
irrelevant.
 
2169
 
 
2170
This test shows the interaction with the zc.buildout.easy_install API.  Another
 
2171
test below (allow_site_package_eggs_option) shows using it with a buildout.cfg.
 
2172
 
 
2173
Our "py_path" has the "demoneeded" and "demo" packages available.  We'll
 
2174
simply be asking for "demoneeded" here.
 
2175
 
 
2176
    >>> py_path, site_packages_path = make_py()
 
2177
    >>> create_sample_sys_install(site_packages_path)
 
2178
 
 
2179
    >>> example_dest = tmpdir('site-packages-example-install')
 
2180
    >>> workingset = zc.buildout.easy_install.install(
 
2181
    ...     ['demoneeded'], example_dest, links=[], executable=py_path,
 
2182
    ...     index=None,
 
2183
    ...     allowed_eggs_from_site_packages=['demoneeded', 'other'])
 
2184
    >>> [dist.project_name for dist in workingset]
 
2185
    ['demoneeded']
 
2186
 
 
2187
That worked fine.  It would work fine for a glob too.
 
2188
 
 
2189
    >>> zc.buildout.easy_install.clear_index_cache()
 
2190
    >>> rmdir(example_dest)
 
2191
    >>> example_dest = tmpdir('site-packages-example-install')
 
2192
    >>> workingset = zc.buildout.easy_install.install(
 
2193
    ...     ['demoneeded'], example_dest, links=[], executable=py_path,
 
2194
    ...     index=None,
 
2195
    ...     allowed_eggs_from_site_packages=['?emon*', 'other'])
 
2196
    >>> [dist.project_name for dist in workingset]
 
2197
    ['demoneeded']
 
2198
 
 
2199
But now let's try again with 'demoneeded' not allowed.
 
2200
 
 
2201
    >>> zc.buildout.easy_install.clear_index_cache()
 
2202
    >>> rmdir(example_dest)
 
2203
    >>> example_dest = tmpdir('site-packages-example-install')
 
2204
    >>> workingset = zc.buildout.easy_install.install(
 
2205
    ...     ['demoneeded'], example_dest, links=[], executable=py_path,
 
2206
    ...     index=None,
 
2207
    ...     allowed_eggs_from_site_packages=['demo'])
 
2208
    Traceback (most recent call last):
 
2209
        ...
 
2210
    MissingDistribution: Couldn't find a distribution for 'demoneeded'.
 
2211
 
 
2212
Here's the same, but with an empty list.
 
2213
 
 
2214
    >>> zc.buildout.easy_install.clear_index_cache()
 
2215
    >>> rmdir(example_dest)
 
2216
    >>> example_dest = tmpdir('site-packages-example-install')
 
2217
    >>> workingset = zc.buildout.easy_install.install(
 
2218
    ...     ['demoneeded'], example_dest, links=[], executable=py_path,
 
2219
    ...     index=None,
 
2220
    ...     allowed_eggs_from_site_packages=[])
 
2221
    Traceback (most recent call last):
 
2222
        ...
 
2223
    MissingDistribution: Couldn't find a distribution for 'demoneeded'.
 
2224
 
 
2225
Of course, this doesn't stop us from getting a package from elsewhere.  Here,
 
2226
we add a link server.
 
2227
 
 
2228
    >>> zc.buildout.easy_install.clear_index_cache()
 
2229
    >>> rmdir(example_dest)
 
2230
    >>> example_dest = tmpdir('site-packages-example-install')
 
2231
    >>> workingset = zc.buildout.easy_install.install(
 
2232
    ...     ['demoneeded'], example_dest, executable=py_path,
 
2233
    ...     links=[link_server], index=link_server+'index/',
 
2234
    ...     allowed_eggs_from_site_packages=['other'])
 
2235
    >>> [dist.project_name for dist in workingset]
 
2236
    ['demoneeded']
 
2237
    >>> [dist.location for dist in workingset]
 
2238
    ['/site-packages-example-install/demoneeded-1.1-py2.6.egg']
 
2239
 
 
2240
Finally, here's an example of an interaction: we say that it is OK to
 
2241
allow the "demoneeded" egg to come from site-packages, but we don't
 
2242
include-site-packages.
 
2243
 
 
2244
    >>> zc.buildout.easy_install.clear_index_cache()
 
2245
    >>> rmdir(example_dest)
 
2246
    >>> example_dest = tmpdir('site-packages-example-install')
 
2247
    >>> workingset = zc.buildout.easy_install.install(
 
2248
    ...     ['demoneeded'], example_dest, links=[], executable=py_path,
 
2249
    ...     index=None, include_site_packages=False,
 
2250
    ...     allowed_eggs_from_site_packages=['demoneeded'])
 
2251
    Traceback (most recent call last):
 
2252
        ...
 
2253
    MissingDistribution: Couldn't find a distribution for 'demoneeded'.
 
2254
 
 
2255
    """
 
2256
 
 
2257
def subprocesses_have_same_environment_by_default():
 
2258
    """
 
2259
The scripts generated by sitepackage_safe_scripts set the PYTHONPATH so that,
 
2260
if the environment is maintained (the default behavior), subprocesses get
 
2261
the same Python packages.
 
2262
 
 
2263
First, we set up a script and an interpreter.
 
2264
 
 
2265
    >>> interpreter_dir = tmpdir('interpreter')
 
2266
    >>> interpreter_parts_dir = os.path.join(
 
2267
    ...     interpreter_dir, 'parts', 'interpreter')
 
2268
    >>> interpreter_bin_dir = os.path.join(interpreter_dir, 'bin')
 
2269
    >>> mkdir(interpreter_bin_dir)
 
2270
    >>> mkdir(interpreter_dir, 'eggs')
 
2271
    >>> mkdir(interpreter_dir, 'parts')
 
2272
    >>> mkdir(interpreter_parts_dir)
 
2273
    >>> ws = zc.buildout.easy_install.install(
 
2274
    ...     ['demo'], join(interpreter_dir, 'eggs'), links=[link_server],
 
2275
    ...     index=link_server+'index/')
 
2276
    >>> test = (
 
2277
    ...     "import subprocess, sys; subprocess.call("
 
2278
    ...     "[sys.executable, '-c', "
 
2279
    ...     "'import eggrecipedemo; print eggrecipedemo.x'])")
 
2280
    >>> generated = zc.buildout.easy_install.sitepackage_safe_scripts(
 
2281
    ...     interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
 
2282
    ...     reqs=['demo'], interpreter='py',
 
2283
    ...     script_initialization=test + '; sys.exit(0)')
 
2284
 
 
2285
This works for the script.
 
2286
 
 
2287
    >>> print system(join(interpreter_bin_dir, 'demo'))
 
2288
    3
 
2289
    <BLANKLINE>
 
2290
 
 
2291
This also works for the generated interpreter.
 
2292
 
 
2293
    >>> print call_py(join(interpreter_bin_dir, 'py'), test)
 
2294
    3
 
2295
    <BLANKLINE>
 
2296
 
 
2297
    """
 
2298
 
 
2299
def bootstrap_makes_buildout_that_works_with_system_python():
 
2300
    r"""
 
2301
In order to work smoothly with a system Python, bootstrapping creates
 
2302
the buildout script with
 
2303
zc.buildout.easy_install.sitepackage_safe_scripts. If it did not, a
 
2304
variety of problems might happen.  For instance, if another version of
 
2305
buildout or setuptools is installed in the site-packages than is
 
2306
desired, it may cause a problem.
 
2307
 
 
2308
A problem actually experienced in the field is when
 
2309
a recipe wants a different version of a dependency that is installed in
 
2310
site-packages.  We will create a similar situation, and show that it is now
 
2311
handled.
 
2312
 
 
2313
First let's write a dummy recipe.
 
2314
 
 
2315
    >>> mkdir(sample_buildout, 'recipes')
 
2316
    >>> write(sample_buildout, 'recipes', 'dummy.py',
 
2317
    ... '''
 
2318
    ... import logging, os, zc.buildout
 
2319
    ... class Dummy:
 
2320
    ...     def __init__(self, buildout, name, options):
 
2321
    ...         pass
 
2322
    ...     def install(self):
 
2323
    ...         return ()
 
2324
    ...     def update(self):
 
2325
    ...         pass
 
2326
    ... ''')
 
2327
    >>> write(sample_buildout, 'recipes', 'setup.py',
 
2328
    ... '''
 
2329
    ... from setuptools import setup
 
2330
    ...
 
2331
    ... setup(
 
2332
    ...     name = "recipes",
 
2333
    ...     entry_points = {'zc.buildout': ['dummy = dummy:Dummy']},
 
2334
    ...     install_requires = 'demoneeded==1.2c1',
 
2335
    ...     )
 
2336
    ... ''')
 
2337
    >>> write(sample_buildout, 'recipes', 'README.txt', " ")
 
2338
 
 
2339
Now we'll try to use it with a Python that has a different version of
 
2340
demoneeded installed.
 
2341
 
 
2342
    >>> py_path, site_packages_path = make_py()
 
2343
    >>> create_sample_sys_install(site_packages_path)
 
2344
    >>> rmdir('develop-eggs')
 
2345
    >>> from zc.buildout.testing import make_buildout
 
2346
    >>> make_buildout(executable=py_path)
 
2347
    >>> write(sample_buildout, 'buildout.cfg',
 
2348
    ... '''
 
2349
    ... [buildout]
 
2350
    ... develop = recipes
 
2351
    ... parts = dummy
 
2352
    ... find-links = %(link_server)s
 
2353
    ... executable = %(py_path)s
 
2354
    ...
 
2355
    ... [dummy]
 
2356
    ... recipe = recipes:dummy
 
2357
    ... ''' % globals())
 
2358
 
 
2359
Now we actually run the buildout.  Before the change, we got the following
 
2360
error:
 
2361
 
 
2362
    Develop: '/sample-buildout/recipes'
 
2363
    While:
 
2364
      Installing.
 
2365
      Getting section dummy.
 
2366
      Initializing section dummy.
 
2367
      Installing recipe recipes.
 
2368
    Error: There is a version conflict.
 
2369
    We already have: demoneeded 1.1
 
2370
    but recipes 0.0.0 requires 'demoneeded==1.2c1'.
 
2371
 
 
2372
Now, it is handled smoothly.
 
2373
 
 
2374
    >>> print system(buildout)
 
2375
    Develop: '/sample-buildout/recipes'
 
2376
    Getting distribution for 'demoneeded==1.2c1'.
 
2377
    Got demoneeded 1.2c1.
 
2378
    Installing dummy.
 
2379
    <BLANKLINE>
 
2380
 
 
2381
Here's the same story with a namespace package, which has some additional
 
2382
complications behind the scenes.  First, a recipe, in the "tellmy" namespace.
 
2383
 
 
2384
    >>> mkdir(sample_buildout, 'ns')
 
2385
    >>> mkdir(sample_buildout, 'ns', 'tellmy')
 
2386
    >>> write(sample_buildout, 'ns', 'tellmy', '__init__.py',
 
2387
    ...       "__import__('pkg_resources').declare_namespace(__name__)\n")
 
2388
    >>> mkdir(sample_buildout, 'ns', 'tellmy', 'recipes')
 
2389
    >>> write(sample_buildout, 'ns', 'tellmy', 'recipes', '__init__.py', ' ')
 
2390
    >>> write(sample_buildout, 'ns', 'tellmy', 'recipes', 'dummy.py',
 
2391
    ... '''
 
2392
    ... import logging, os, zc.buildout
 
2393
    ... class Dummy:
 
2394
    ...     def __init__(self, buildout, name, options):
 
2395
    ...         pass
 
2396
    ...     def install(self):
 
2397
    ...         return ()
 
2398
    ...     def update(self):
 
2399
    ...         pass
 
2400
    ... ''')
 
2401
    >>> write(sample_buildout, 'ns', 'setup.py',
 
2402
    ... '''
 
2403
    ... from setuptools import setup
 
2404
    ... setup(
 
2405
    ...     name="tellmy.recipes",
 
2406
    ...     packages=['tellmy', 'tellmy.recipes'],
 
2407
    ...     install_requires=['setuptools'],
 
2408
    ...     namespace_packages=['tellmy'],
 
2409
    ...     entry_points = {'zc.buildout':
 
2410
    ...                     ['dummy = tellmy.recipes.dummy:Dummy']},
 
2411
    ...     )
 
2412
    ... ''')
 
2413
 
 
2414
Now, a buildout that uses it.
 
2415
 
 
2416
    >>> create_sample_namespace_eggs(sample_eggs, site_packages_path)
 
2417
    >>> rmdir('develop-eggs')
 
2418
    >>> from zc.buildout.testing import make_buildout
 
2419
    >>> make_buildout(executable=py_path)
 
2420
    >>> write(sample_buildout, 'buildout.cfg',
 
2421
    ... '''
 
2422
    ... [buildout]
 
2423
    ... develop = ns
 
2424
    ...           recipes
 
2425
    ... parts = dummy
 
2426
    ... find-links = %(link_server)s
 
2427
    ... executable = %(py_path)s
 
2428
    ...
 
2429
    ... [dummy]
 
2430
    ... recipe = tellmy.recipes:dummy
 
2431
    ... ''' % globals())
 
2432
 
 
2433
Now we actually run the buildout.
 
2434
 
 
2435
    >>> print system(buildout)
 
2436
    Develop: '/sample-buildout/ns'
 
2437
    Develop: '/sample-buildout/recipes'
 
2438
    Uninstalling dummy.
 
2439
    Installing dummy.
 
2440
    <BLANKLINE>
 
2441
 
 
2442
    """
 
2443
 
1772
2444
if sys.version_info > (2, 4):
1773
2445
    def test_exit_codes():
1774
2446
        """
2367
3039
 
2368
3040
    >>> ls('develop-eggs')
2369
3041
    -  foo.egg-link
 
3042
    -  z3c.recipe.scripts.egg-link
2370
3043
    -  zc.recipe.egg.egg-link
2371
3044
 
2372
3045
    """
2654
3327
 
2655
3328
######################################################################
2656
3329
 
 
3330
def make_py_with_system_install(make_py, sample_eggs):
 
3331
    py_path, site_packages_path = make_py()
 
3332
    create_sample_namespace_eggs(sample_eggs, site_packages_path)
 
3333
    return py_path
 
3334
 
 
3335
def create_sample_namespace_eggs(dest, site_packages_path=None):
 
3336
    from zc.buildout.testing import write, mkdir
 
3337
    for pkg, version in (('version', '1.0'), ('version', '1.1'),
 
3338
                         ('fortune', '1.0')):
 
3339
        tmp = tempfile.mkdtemp()
 
3340
        try:
 
3341
            write(tmp, 'README.txt', '')
 
3342
            mkdir(tmp, 'src')
 
3343
            mkdir(tmp, 'src', 'tellmy')
 
3344
            write(tmp, 'src', 'tellmy', '__init__.py',
 
3345
                "__import__("
 
3346
                "'pkg_resources').declare_namespace(__name__)\n")
 
3347
            mkdir(tmp, 'src', 'tellmy', pkg)
 
3348
            write(tmp, 'src', 'tellmy', pkg,
 
3349
                  '__init__.py', '__version__=%r\n' % version)
 
3350
            write(
 
3351
                tmp, 'setup.py',
 
3352
                "from setuptools import setup\n"
 
3353
                "setup(\n"
 
3354
                " name='tellmy.%(pkg)s',\n"
 
3355
                " package_dir = {'': 'src'},\n"
 
3356
                " packages = ['tellmy', 'tellmy.%(pkg)s'],\n"
 
3357
                " install_requires = ['setuptools'],\n"
 
3358
                " namespace_packages=['tellmy'],\n"
 
3359
                " zip_safe=True, version=%(version)r,\n"
 
3360
                " author='bob', url='bob', author_email='bob')\n"
 
3361
                % locals()
 
3362
                )
 
3363
            zc.buildout.testing.sdist(tmp, dest)
 
3364
            if (site_packages_path and pkg == 'version' and version == '1.1'):
 
3365
                # We install the 1.1 version in site packages the way a
 
3366
                # system packaging system (debs, rpms) would do it.
 
3367
                zc.buildout.testing.sys_install(tmp, site_packages_path)
 
3368
        finally:
 
3369
            shutil.rmtree(tmp)
 
3370
 
 
3371
def _write_eggrecipedemoneeded(tmp, minor_version, suffix=''):
 
3372
    from zc.buildout.testing import write
 
3373
    write(tmp, 'README.txt', '')
 
3374
    write(tmp, 'eggrecipedemoneeded.py',
 
3375
          'y=%s\ndef f():\n  pass' % minor_version)
 
3376
    write(
 
3377
        tmp, 'setup.py',
 
3378
        "from setuptools import setup\n"
 
3379
        "setup(name='demoneeded', py_modules=['eggrecipedemoneeded'],"
 
3380
        " zip_safe=True, version='1.%s%s', author='bob', url='bob', "
 
3381
        "author_email='bob')\n"
 
3382
        % (minor_version, suffix)
 
3383
        )
 
3384
 
 
3385
def _write_eggrecipedemo(tmp, minor_version, suffix=''):
 
3386
    from zc.buildout.testing import write
 
3387
    write(tmp, 'README.txt', '')
 
3388
    write(
 
3389
        tmp, 'eggrecipedemo.py',
 
3390
        'import eggrecipedemoneeded\n'
 
3391
        'x=%s\n'
 
3392
        'def main(): print x, eggrecipedemoneeded.y\n'
 
3393
        % minor_version)
 
3394
    write(
 
3395
        tmp, 'setup.py',
 
3396
        "from setuptools import setup\n"
 
3397
        "setup(name='demo', py_modules=['eggrecipedemo'],"
 
3398
        " install_requires = 'demoneeded',"
 
3399
        " entry_points={'console_scripts': "
 
3400
             "['demo = eggrecipedemo:main']},"
 
3401
        " zip_safe=True, version='0.%s%s')\n" % (minor_version, suffix)
 
3402
        )
 
3403
 
 
3404
def create_sample_sys_install(site_packages_path):
 
3405
    for creator, minor_version in (
 
3406
        (_write_eggrecipedemoneeded, 1),
 
3407
        (_write_eggrecipedemo, 3)):
 
3408
        # Write the files and install in site_packages_path.
 
3409
        tmp = tempfile.mkdtemp()
 
3410
        try:
 
3411
            creator(tmp, minor_version)
 
3412
            zc.buildout.testing.sys_install(tmp, site_packages_path)
 
3413
        finally:
 
3414
            shutil.rmtree(tmp)
 
3415
 
2657
3416
def create_sample_eggs(test, executable=sys.executable):
2658
 
    write = test.globs['write']
 
3417
    from zc.buildout.testing import write
2659
3418
    dest = test.globs['sample_eggs']
2660
3419
    tmp = tempfile.mkdtemp()
2661
3420
    try:
2662
 
        write(tmp, 'README.txt', '')
2663
 
 
2664
3421
        for i in (0, 1, 2):
2665
 
            write(tmp, 'eggrecipedemoneeded.py', 'y=%s\ndef f():\n  pass' % i)
2666
 
            c1 = i==2 and 'c1' or ''
2667
 
            write(
2668
 
                tmp, 'setup.py',
2669
 
                "from setuptools import setup\n"
2670
 
                "setup(name='demoneeded', py_modules=['eggrecipedemoneeded'],"
2671
 
                " zip_safe=True, version='1.%s%s', author='bob', url='bob', "
2672
 
                "author_email='bob')\n"
2673
 
                % (i, c1)
2674
 
                )
 
3422
            suffix = i==2 and 'c1' or ''
 
3423
            _write_eggrecipedemoneeded(tmp, i, suffix)
2675
3424
            zc.buildout.testing.sdist(tmp, dest)
2676
3425
 
2677
3426
        write(
2685
3434
        os.remove(os.path.join(tmp, 'eggrecipedemoneeded.py'))
2686
3435
 
2687
3436
        for i in (1, 2, 3, 4):
2688
 
            write(
2689
 
                tmp, 'eggrecipedemo.py',
2690
 
                'import eggrecipedemoneeded\n'
2691
 
                'x=%s\n'
2692
 
                'def main(): print x, eggrecipedemoneeded.y\n'
2693
 
                % i)
2694
 
            c1 = i==4 and 'c1' or ''
2695
 
            write(
2696
 
                tmp, 'setup.py',
2697
 
                "from setuptools import setup\n"
2698
 
                "setup(name='demo', py_modules=['eggrecipedemo'],"
2699
 
                " install_requires = 'demoneeded',"
2700
 
                " entry_points={'console_scripts': "
2701
 
                     "['demo = eggrecipedemo:main']},"
2702
 
                " zip_safe=True, version='0.%s%s')\n" % (i, c1)
2703
 
                )
 
3437
            suffix = i==4 and 'c1' or ''
 
3438
            _write_eggrecipedemo(tmp, i, suffix)
2704
3439
            zc.buildout.testing.bdist_egg(tmp, executable, dest)
2705
3440
 
2706
3441
        write(tmp, 'eggrecipebigdemo.py', 'import eggrecipedemo')
2776
3511
        test.globs['sample_eggs'])
2777
3512
    test.globs['update_extdemo'] = lambda : add_source_dist(test, 1.5)
2778
3513
    zc.buildout.testing.install_develop('zc.recipe.egg', test)
 
3514
    zc.buildout.testing.install_develop('z3c.recipe.scripts', test)
2779
3515
 
2780
3516
egg_parse = re.compile('([0-9a-zA-Z_.]+)-([0-9a-zA-Z_.]+)-py(\d[.]\d).egg$'
2781
3517
                       ).match
2818
3554
        here = os.getcwd()
2819
3555
        os.chdir(os.path.dirname(dist.location))
2820
3556
        assert os.spawnle(
2821
 
            os.P_WAIT, sys.executable, zc.buildout.easy_install._safe_arg (sys.executable),
 
3557
            os.P_WAIT, sys.executable,
 
3558
            zc.buildout.easy_install._safe_arg(sys.executable),
2822
3559
            os.path.join(os.path.dirname(dist.location), 'setup.py'),
2823
3560
            '-q', 'bdist_egg', '-d', eggs,
2824
3561
            dict(os.environ,
2934
3671
               (re.compile('[-d]  setuptools-\S+[.]egg'), 'setuptools.egg'),
2935
3672
               (re.compile(r'\\[\\]?'), '/'),
2936
3673
               (re.compile(r'\#!\S+\bpython\S*'), '#!/usr/bin/python'),
 
3674
               # Normalize generate_script's Windows interpreter to UNIX:
 
3675
               (re.compile(r'\nimport subprocess\n'), '\n'),
 
3676
               (re.compile('subprocess\\.call\\(argv, env=environ\\)'),
 
3677
                'os.execve(sys.executable, argv, environ)'),
2937
3678
               ]+(sys.version_info < (2, 5) and [
2938
3679
                  (re.compile('.*No module named runpy.*', re.S), ''),
2939
3680
                  (re.compile('.*usage: pdb.py scriptfile .*', re.S), ''),
2981
3722
                   '-q develop -mxN -d /sample-buildout/develop-eggs'
2982
3723
                ),
2983
3724
               (re.compile(r'^[*]...'), '...'),
 
3725
               # for bug_92891_bootstrap_crashes_with_egg_recipe_in_buildout_section
 
3726
               (re.compile(r"Unused options for buildout: 'eggs' 'scripts'\."),
 
3727
                "Unused options for buildout: 'scripts' 'eggs'."),
2984
3728
               ]),
2985
3729
            ),
2986
3730
        zc.buildout.testselectingpython.test_suite(),
3040
3784
               zc.buildout.testing.normalize_script,
3041
3785
               normalize_bang,
3042
3786
               (re.compile('Downloading.*setuptools.*egg\n'), ''),
 
3787
               (re.compile('options:'), 'Options:'),
 
3788
               (re.compile('usage:'), 'Usage:'),
3043
3789
               ]),
3044
3790
            ))
3045
3791