[Checkins] SVN: zc.buildout/trunk/ - If a distribution defines namespace packages but fails to declare

Jim Fulton jim at zope.com
Sun Oct 8 15:27:45 EDT 2006


Log message for revision 70578:
  - If a distribution defines namespace packages but fails to declare 
    setuptools as one of its dependencies, we now treat setuptools as an 
    implicit dependency.  We generate a warning if the distribution
    is a develop egg.
  
  - Remove old develop egg links. This requires storing the old link
    paths in .installed.cfg.
  

Changed:
  U   zc.buildout/trunk/CHANGES.txt
  U   zc.buildout/trunk/src/zc/buildout/buildout.py
  U   zc.buildout/trunk/src/zc/buildout/buildout.txt
  U   zc.buildout/trunk/src/zc/buildout/easy_install.py
  U   zc.buildout/trunk/src/zc/buildout/tests.py
  U   zc.buildout/trunk/zc.recipe.egg_/src/zc/recipe/egg/api.txt
  U   zc.buildout/trunk/zc.recipe.egg_/src/zc/recipe/egg/tests.py
  U   zc.buildout/trunk/zc.recipe.testrunner/src/zc/recipe/testrunner/README.txt
  U   zc.buildout/trunk/zc.recipe.testrunner/src/zc/recipe/testrunner/tests.py

-=-
Modified: zc.buildout/trunk/CHANGES.txt
===================================================================
--- zc.buildout/trunk/CHANGES.txt	2006-10-08 16:24:54 UTC (rev 70577)
+++ zc.buildout/trunk/CHANGES.txt	2006-10-08 19:27:44 UTC (rev 70578)
@@ -33,6 +33,17 @@
   update is called.  For backward compatibility, recipes that don't
   define update methiods are still supported.
 
+- If a distribution defines namespace packages but fails to declare 
+  setuptools as one of its dependencies, we now treat setuptools as an 
+  implicit dependency.  We generate a warning if the distribution
+  is a develop egg.
+
+Bugs Fixed
+----------
+
+- Egg links weren't removed when corresponding entries were removed
+  from develop sections.
+
 1.0.0b9 (2006-10-02)
 ====================
 

Modified: zc.buildout/trunk/src/zc/buildout/buildout.py
===================================================================
--- zc.buildout/trunk/src/zc/buildout/buildout.py	2006-10-08 16:24:54 UTC (rev 70577)
+++ zc.buildout/trunk/src/zc/buildout/buildout.py	2006-10-08 19:27:44 UTC (rev 70578)
@@ -251,12 +251,18 @@
         # Check for updates. This could cause the process to be rstarted
         self._maybe_upgrade()
 
-        # Build develop eggs
-        self._develop()
-
         # load installed data
         installed_part_options = self._read_installed_part_options()
 
+        # Remove old develop eggs
+        self._uninstall(
+            installed_part_options['buildout'].get(
+                'installed_develop_eggs', '')
+            )
+
+        # Build develop eggs
+        installed_develop_eggs = self._develop()
+
         # get configured and installed part lists
         conf_parts = self['buildout']['parts']
         conf_parts = conf_parts and conf_parts.split() or []
@@ -380,6 +386,9 @@
                 +
                 [p for p in installed_parts if p not in conf_parts] 
             )
+            installed_part_options['buildout']['installed_develop_eggs'
+                                               ] = installed_develop_eggs
+            
             self._save_installed_options(installed_part_options)
 
     def _setup_directories(self):
@@ -395,9 +404,15 @@
         """Install sources by running setup.py develop on them
         """
         develop = self['buildout'].get('develop')
-        if develop:
-            env = dict(os.environ, PYTHONPATH=pkg_resources_loc)
-            here = os.getcwd()
+        if not develop:
+            return ''
+
+        dest = self['buildout']['develop-eggs-directory']
+        old_files = os.listdir(dest)
+
+        env = dict(os.environ, PYTHONPATH=pkg_resources_loc)
+        here = os.getcwd()
+        try:
             try:
                 for setup in develop.split():
                     setup = self._buildout_path(setup)
@@ -413,9 +428,7 @@
                         '-f', zc.buildout.easy_install._safe_arg(
                             ' '.join(self._links)
                                   ),
-                        '-d', zc.buildout.easy_install._safe_arg(
-                                  self['buildout']['develop-eggs-directory']
-                                  ),
+                        '-d', zc.buildout.easy_install._safe_arg(dest),
                         ]
 
                     if self._log_level <= logging.DEBUG:
@@ -425,12 +438,40 @@
                             args[1] == '-v'
                         self._logger.debug("in: %s\n%r",
                                            os.path.dirname(setup), args)
-                        
+
                     args.append(env)
-                    os.spawnle(os.P_WAIT, sys.executable, sys.executable, *args)
-            finally:
-                os.chdir(here)
+                    assert os.spawnle(
+                        os.P_WAIT, sys.executable, sys.executable, *args
+                        ) == 0
+            except:
+                # if we had an error, we need to roll back changes, by
+                # removing any files we created.
+                self._sanity_check_develop_eggs_files(dest, old_files)
+                self._uninstall('\n'.join(
+                    [os.path.join(dest, f)
+                     for f in os.listdir(dest)
+                     if f not in old_files
+                     ]))
+            else:
+                self._sanity_check_develop_eggs_files(dest, old_files)
+                return '\n'.join([os.path.join(dest, f)
+                                  for f in os.listdir(dest)
+                                  if f not in old_files
+                                  ])
 
+        finally:
+            os.chdir(here)
+
+
+    def _sanity_check_develop_eggs_files(self, dest, old_files):
+        for f in os.listdir(dest):
+            if f in old_files:
+                continue
+            if not (os.path.isfile(os.path.join(dest, f))
+                    and f.endswith('.egg-link')):
+                self._logger.warning(
+                    "Unexpected entry, %s, in develop-eggs directory", f)
+
     def _load_recipes(self, parts):
         recipes = {}
         if not parts:

Modified: zc.buildout/trunk/src/zc/buildout/buildout.txt
===================================================================
--- zc.buildout/trunk/src/zc/buildout/buildout.txt	2006-10-08 16:24:54 UTC (rev 70577)
+++ zc.buildout/trunk/src/zc/buildout/buildout.txt	2006-10-08 19:27:44 UTC (rev 70578)
@@ -307,6 +307,7 @@
 
     >>> cat(sample_buildout, '.installed.cfg')
     [buildout]
+    installed_develop_eggs = /sample-buildout/develop-eggs/recipes.egg-link
     parts = data-dir
     <BLANKLINE>
     [data-dir]
@@ -904,6 +905,7 @@
 
     >>> cat(sample_buildout, '.installed.cfg')
     [buildout]
+    installed_develop_eggs = /sample-buildout/develop-eggs/recipes.egg-link
     parts = debug d1 d2 d3
     <BLANKLINE>
     [debug]
@@ -988,6 +990,7 @@
 
     >>> cat(sample_buildout, '.installed.cfg')
     [buildout]
+    installed_develop_eggs = /sample-buildout/develop-eggs/recipes.egg-link
     parts = debug d2 d3 d4 d1
     <BLANKLINE>
     [debug]

Modified: zc.buildout/trunk/src/zc/buildout/easy_install.py
===================================================================
--- zc.buildout/trunk/src/zc/buildout/easy_install.py	2006-10-08 16:24:54 UTC (rev 70577)
+++ zc.buildout/trunk/src/zc/buildout/easy_install.py	2006-10-08 19:27:44 UTC (rev 70578)
@@ -294,7 +294,7 @@
             link = link.strip()
             if link not in links:
                 links.append(link)
-
+                
     return dist
     
 def install(specs, dest,
@@ -325,9 +325,28 @@
         ws = working_set
 
     for requirement in requirements:
-        ws.add(_get_dist(requirement, env, ws,
+        dist = _get_dist(requirement, env, ws,
                          dest, links, index, executable, always_unzip)
-               )
+        ws.add(dist)
+        if dist.has_metadata('namespace_packages.txt'):
+            for r in dist.requires():
+                if r.project_name == 'setuptools':
+                    break
+            else:
+                # We have a namespace package but no requirement for setuptools
+                if dist.precedence == pkg_resources.DEVELOP_DIST:
+                    logger.warn(
+                        "Develop distribution for %s\n"
+                        "uses namespace packages but the distribution "
+                        "does not require setuptools.",
+                        dist)
+                requirement = pkg_resources.Requirement.parse('setuptools')
+                if ws.find(requirement) is None:
+                    dist = _get_dist(requirement, env, ws,
+                                     dest, links, index, executable,
+                                     False)
+                    ws.add(dist)
+                    
 
     # OK, we have the requested distributions and they're in the working
     # set, but they may have unmet requirements.  We'll simply keep

Modified: zc.buildout/trunk/src/zc/buildout/tests.py
===================================================================
--- zc.buildout/trunk/src/zc/buildout/tests.py	2006-10-08 16:24:54 UTC (rev 70577)
+++ zc.buildout/trunk/src/zc/buildout/tests.py	2006-10-08 19:27:44 UTC (rev 70578)
@@ -382,7 +382,179 @@
     buildout: Creating directory /sample-bootstrap/develop-eggs
     """
 
+def removing_eggs_from_develop_section_causes_egg_link_to_be_removed():
+    '''
+    >>> cd(sample_buildout)
 
+Create a develop egg:
+
+    >>> mkdir('foo')
+    >>> write('foo', 'setup.py',
+    ... """
+    ... from setuptools import setup
+    ... setup(name='foox')
+    ... """)
+    >>> write('buildout.cfg',
+    ... """
+    ... [buildout]
+    ... develop = foo
+    ... parts =
+    ... """)
+
+    >>> print system(join('bin', 'buildout')),
+    buildout: Develop: /sample-buildout/foo/setup.py
+
+    >>> ls('develop-eggs')
+    -  foox.egg-link
+
+Create another:
+
+    >>> mkdir('bar')
+    >>> write('bar', 'setup.py',
+    ... """
+    ... from setuptools import setup
+    ... setup(name='fooy')
+    ... """)
+    >>> write('buildout.cfg',
+    ... """
+    ... [buildout]
+    ... develop = foo bar
+    ... parts =
+    ... """)
+
+    >>> print system(join('bin', 'buildout')),
+    buildout: Develop: /sample-buildout/foo/setup.py
+    buildout: Develop: /sample-buildout/bar/setup.py
+
+    >>> ls('develop-eggs')
+    -  foox.egg-link
+    -  fooy.egg-link
+
+Remove one:
+
+    >>> write('buildout.cfg',
+    ... """
+    ... [buildout]
+    ... develop = bar
+    ... parts =
+    ... """)
+    >>> print system(join('bin', 'buildout')),
+    buildout: Develop: /sample-buildout/bar/setup.py
+
+It is gone
+
+    >>> ls('develop-eggs')
+    -  fooy.egg-link
+
+Remove the other:
+
+    >>> write('buildout.cfg',
+    ... """
+    ... [buildout]
+    ... parts =
+    ... """)
+    >>> print system(join('bin', 'buildout')),
+
+All gone
+
+    >>> ls('develop-eggs')
+    '''
+
+
+def add_setuptools_to_dependencies_when_namespace_packages():
+    '''    
+Often, a package depends on setuptools soley by virtue of using
+namespace packages. In this situation, package authors often forget to
+declare setuptools as a dependency. This is a mistake, but,
+unfortunately, a common one that we need to work around.  If an egg
+uses namespace packages and does not include setuptools as a depenency,
+we willll still include setuptools in the working set.  If we see this for
+a devlop egg, we will also generate a warning.
+
+    >>> cd(sample_buildout)
+
+    >>> mkdir('foo')
+    >>> mkdir('foo', 'src')
+    >>> mkdir('foo', 'src', 'stuff')
+    >>> write('foo', 'src', 'stuff', '__init__.py',
+    ... """__import__('pkg_resources').declare_namespace(__name__)
+    ... """)
+    >>> mkdir('foo', 'src', 'stuff', 'foox')
+    >>> write('foo', 'src', 'stuff', 'foox', '__init__.py', '')
+    >>> write('foo', 'setup.py',
+    ... """
+    ... from setuptools import setup
+    ... setup(name='foox',
+    ...       namespace_packages = ['stuff'],
+    ...       package_dir = {'': 'src'},
+    ...       packages = ['stuff', 'stuff.foox'],
+    ...       )
+    ... """)
+    >>> write('foo', 'README.txt', '')
+    
+    >>> write('buildout.cfg',
+    ... """
+    ... [buildout]
+    ... develop = foo
+    ... parts = 
+    ... """)
+
+    >>> print system(join('bin', 'buildout')),
+    buildout: Develop: /sample-buildout/foo/setup.py
+
+Now, if we generate a working set using the egg link, we will get a warning
+and we will get setuptools included in the working set.
+
+    >>> import logging, zope.testing.loggingsupport
+    >>> handler = zope.testing.loggingsupport.InstalledHandler(
+    ...        'zc.buildout', level=logging.WARNING)
+    >>> logging.getLogger('zc').propagate = False
+    
+    >>> [dist.project_name
+    ...  for dist in zc.buildout.easy_install.working_set(
+    ...    ['foox'], sys.executable,
+    ...    [join(sample_buildout, 'eggs'),
+    ...     join(sample_buildout, 'develop-eggs'),
+    ...     ])]
+    ['foox', 'setuptools']
+
+    >>> print handler
+    zc.buildout.easy_install WARNING
+      Develop distribution for foox 0.0.0
+    uses namespace packages but the distribution does not require setuptools.
+
+    >>> handler.clear()
+
+On the other hand, if we have a regular egg, rather than a develop egg:
+
+    >>> os.remove(join('develop-eggs', 'foox.egg-link'))
+
+    >>> _ = system(join('bin', 'buildout') + ' setup foo bdist_egg -d'
+    ...            + join(sample_buildout, 'eggs'))
+
+    >>> ls('develop-eggs')
+    
+    >>> ls('eggs') # doctest: +ELLIPSIS
+    -  foox-0.0.0-py2.4.egg
+    ...
+    
+We do not get a warning, but we do get setuptools included in the working set:
+
+    >>> [dist.project_name
+    ...  for dist in zc.buildout.easy_install.working_set(
+    ...    ['foox'], sys.executable,
+    ...    [join(sample_buildout, 'eggs'),
+    ...     join(sample_buildout, 'develop-eggs'),
+    ...     ])]
+    ['foox', 'setuptools']
+
+
+    >>> print handler,
+
+    >>> logging.getLogger('zc').propagate = True
+    >>> handler.uninstall()
+    '''
+    
 def create_sample_eggs(test, executable=sys.executable):
     write = test.globs['write']
     dest = test.globs['sample_eggs']
@@ -603,7 +775,6 @@
         doctest.DocTestSuite(
             setUp=zc.buildout.testing.buildoutSetUp,
             tearDown=zc.buildout.testing.buildoutTearDown,
-
             checker=renormalizing.RENormalizing([
                zc.buildout.testing.normalize_path,
                zc.buildout.testing.normalize_script,

Modified: zc.buildout/trunk/zc.recipe.egg_/src/zc/recipe/egg/api.txt
===================================================================
--- zc.buildout/trunk/zc.recipe.egg_/src/zc/recipe/egg/api.txt	2006-10-08 16:24:54 UTC (rev 70577)
+++ zc.buildout/trunk/zc.recipe.egg_/src/zc/recipe/egg/api.txt	2006-10-08 19:27:44 UTC (rev 70578)
@@ -96,6 +96,7 @@
 
     >>> cat(sample_buildout, '.installed.cfg')
     [buildout]
+    installed_develop_eggs = /sample-buildout/develop-eggs/sample.egg-link
     parts = sample-part
     <BLANKLINE>
     [sample-part]
@@ -104,9 +105,9 @@
             zc.recipe.egg-cAsnudgkduAa/Fd+WJIM6Q==
             setuptools-0.6-py2.4.egg
             zc.buildout-+rYeCcmFuD1K/aB77XTj5A==
-    _b = /tmp/tmpb7kP9bsample-buildout/bin
-    _d = /tmp/tmpb7kP9bsample-buildout/develop-eggs
-    _e = /tmp/tmpb7kP9bsample-buildout/eggs
+    _b = /sample-buildout/bin
+    _d = /sample-buildout/develop-eggs
+    _e = /sample-buildout/eggs
     eggs = demo<0.3
     executable = /usr/local/bin/python2.3
     extras = other

Modified: zc.buildout/trunk/zc.recipe.egg_/src/zc/recipe/egg/tests.py
===================================================================
--- zc.buildout/trunk/zc.recipe.egg_/src/zc/recipe/egg/tests.py	2006-10-08 16:24:54 UTC (rev 70577)
+++ zc.buildout/trunk/zc.recipe.egg_/src/zc/recipe/egg/tests.py	2006-10-08 19:27:44 UTC (rev 70578)
@@ -56,8 +56,7 @@
             'api.txt',
             setUp=setUp, tearDown=zc.buildout.testing.buildoutTearDown,
             checker=renormalizing.RENormalizing([
-               (re.compile('_b = \S+sample-buildout.bin'),
-                '_b = sample-buildout/bin'),
+               zc.buildout.testing.normalize_path,
                (re.compile('__buildout_signature__ = '
                            'sample-\S+\s+'
                            'zc.recipe.egg-\S+\s+'
@@ -65,10 +64,6 @@
                            'zc.buildout-\S+\s*'
                            ),
                 '__buildout_signature__ = sample- zc.recipe.egg-'),
-               (re.compile('_d = \S+sample-buildout.develop-eggs'),
-                '_d = sample-buildout/develop-eggs'),
-               (re.compile('_e = \S+sample-buildout.eggs'),
-                '_e = sample-buildout/eggs'),
                (re.compile('executable = \S+python\S*'),
                 'executable = python'),
                (re.compile('index = \S+python\S+'),

Modified: zc.buildout/trunk/zc.recipe.testrunner/src/zc/recipe/testrunner/README.txt
===================================================================
--- zc.buildout/trunk/zc.recipe.testrunner/src/zc/recipe/testrunner/README.txt	2006-10-08 16:24:54 UTC (rev 70577)
+++ zc.buildout/trunk/zc.recipe.testrunner/src/zc/recipe/testrunner/README.txt	2006-10-08 19:27:44 UTC (rev 70578)
@@ -205,6 +205,7 @@
     sys.path[0:0] = [
       '/sample-buildout/demo',
       '/sample-buildout/eggs/zope.testing-3.0-py2.3.egg',
+      '/sample-buildout/eggs/setuptools-0.6-py1.3.egg',
       '/usr/local/zope/lib/python',
       ]
     <BLANKLINE>
@@ -248,6 +249,7 @@
     sys.path[0:0] = [
       '/sample-buildout/demo',
       '/sample-buildout/eggs/zope.testing-3.0-py2.4.egg',
+      '/sample-buildout/eggs/setuptools-0.6-py1.3.egg',
       '/usr/local/zope/lib/python',
       ]
     <BLANKLINE>

Modified: zc.buildout/trunk/zc.recipe.testrunner/src/zc/recipe/testrunner/tests.py
===================================================================
--- zc.buildout/trunk/zc.recipe.testrunner/src/zc/recipe/testrunner/tests.py	2006-10-08 16:24:54 UTC (rev 70577)
+++ zc.buildout/trunk/zc.recipe.testrunner/src/zc/recipe/testrunner/tests.py	2006-10-08 19:27:44 UTC (rev 70578)
@@ -45,6 +45,7 @@
                (re.compile('#!\S+python\S*'), '#!python'),
                (re.compile('\d[.]\d+ seconds'), '0.001 seconds'),
                (re.compile('zope.testing-[^-]+-'), 'zope.testing-X-'),
+               (re.compile('setuptools-[^-]+-'), 'setuptools-X-'),
                ])
             ),
         



More information about the Checkins mailing list