aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xsetuptools.txt116
-rwxr-xr-xsetuptools/command/easy_install.py4
-rwxr-xr-xsetuptools/command/egg_info.py69
-rw-r--r--setuptools/dist.py93
4 files changed, 239 insertions, 43 deletions
diff --git a/setuptools.txt b/setuptools.txt
index a8e8aa8f..a2e21e7f 100755
--- a/setuptools.txt
+++ b/setuptools.txt
@@ -133,6 +133,56 @@ arguments do (except for the metadata ones), and the various ways you might use
them in your own project(s).
+New and Changed ``setup()`` Keywords
+====================================
+
+The following keyword arguments to ``setup()`` are added or changed by
+``setuptools``. All of them are optional; you do not have to supply them
+unless you need the associated ``setuptools`` feature.
+
+``package_data``
+ A dictionary mapping package names to lists of glob patterns. For a
+ complete description and examples, see the section below on `Including
+ Data Files`_.
+
+``zip_safe``
+ A boolean (True or False) flag specifying whether the project can be
+ safely installed and run from a zip file. If this argument is not
+ supplied, the ``bdist_egg`` command will have to analyze all of your
+ project's contents for possible problems each time it buids an egg.
+
+``install_requires``
+ A string or list of strings specifying what other distributions need to
+ be installed when this one is. See the section below on `Declaring
+ Dependencies`_ for details and examples of the format of this argument.
+
+``extras_require``
+ A dictionary mapping names of "extras" (optional features of your project)
+ to strings or lists of strings specifying what other distributions must be
+ installed to support those features. See the section below on `Declaring
+ Dependencies`_ for details and examples of the format of this argument.
+
+``test_suite``
+ A string naming a ``unittest.TestCase`` subclass (or a module containing
+ one or more of them, or a method of such a subclass), or naming a function
+ that can be called with no arguments and returns a ``unittest.TestSuite``.
+ Specifying this argument enables use of the `test`_ command to run the
+ specified test suite, e.g. via ``setup.py test``. See the section on the
+ `test`_ command below for more details.
+
+``namespace_packages``
+ A list of strings naming the project's "namespace packages". A namespace
+ package is a package that may be split across multiple project
+ distributions. For example, Zope 3's ``zope`` package is a namespace
+ package, because subpackages like ``zope.interface`` and ``zope.publisher``
+ may be distributed separately. The egg runtime system can automatically
+ merge such subpackages into a single parent package at runtime, as long
+ as you declare them in each project that contains any subpackages of the
+ namespace package, and as long as the namespace package's ``__init__.py``
+ does not contain any code. See the section below on `Namespace Packages`_
+ for more information.
+
+
Declaring Dependencies
======================
@@ -406,7 +456,11 @@ a quick example of converting code that uses ``__file__`` to use
.. _Resource Management API: http://peak.telecommunity.com/DevCenter/PythonEggs#resource-management
.. _Accessing Package Resources: http://peak.telecommunity.com/DevCenter/PythonEggs#accessing-package-resources
-.. XXX put doc about zip_safe flag here, once it's implemented
+
+Setting the ``zip_safe`` flag
+-----------------------------
+
+XXX put doc about zip_safe flag here, once it's implemented
"Development Mode"
@@ -483,6 +537,62 @@ Building Extensions written with Pyrex
XXX
+Namespace Packages
+==================
+
+Sometimes, a large package is more useful if distributed as a collection of
+smaller eggs. However, Python does not normally allow the contents of a
+package to be retrieved from more than one location. "Namespace packages"
+are a solution for this problem. When you declare a package to be a namespace
+package, it means that the package has no meaningful contents in its
+``__init__.py``, and that it is merely a container for modules and subpackages.
+
+The ``pkg_resources`` runtime will automatically ensure that the contents of
+namespace packages that are spread over multiple eggs or directories are
+combined into a single virtual package.
+
+The ``namespace_packages`` argument to ``setup()`` lets you declare your
+project's namespace packages, so that they will be included in your project's
+metadata. Then, the runtime will automatically detect this when it adds the
+distribution to ``sys.path``, and ensure that the packages are properly merged.
+
+The argument should list the namespace packages that the egg participates in.
+For example, the ZopeInterface project might do this::
+
+ setup(
+ # ...
+ namespace_packages = ['zope']
+ )
+
+because it contains a ``zope.interface`` package that lives in the ``zope``
+namespace package. Similarly, a project for a standalone ``zope.publisher``
+would also declare the ``zope`` namespace package.
+
+Namespace packages don't have to be top-level packages. For example, Zope 3's
+``zope.app`` package is a namespace package, and in the future PEAK's
+``peak.util`` package will be too.
+
+Note, by the way, that your project's source tree must include the namespace
+packages' ``__init__.py`` files (and the ``__init__.py`` of any parent
+packages), in a normal Python package layout. These ``__init__.py`` files
+should not contain any code or data, because only *one* egg's ``__init__.py``
+files will be used to construct the parent packages in memory at runtime, and
+there is no guarantee which egg will be used.
+
+For example, if both ``zope.interface`` and ``zope.publisher`` have been
+installed from separate distributions, it is unspecified which of the two
+distributions' ``zope/__init__.py`` files will be used to create the ``zope``
+package in memory. Therefore, it is better not to have any code or data in
+a namespace package's ``__init__`` module, so as to prevent any complications.
+
+(This is one reason the concept is called a "namespace package": it is a
+package that exists *only* to provide a namespace under which other modules or
+packages are gathered. In Java, for example, namespace packages are often used
+just to avoid naming collisions between different projects, using package names
+like ``org.apache`` as a namespace for packages that are part of apache.org
+projects.)
+
+
-----------------
Command Reference
-----------------
@@ -916,6 +1026,8 @@ options`_ (listed under the `saveopts`_ command, above) to determine which
distutils configuration file the option will be added to (or removed from).
+.. _test:
+
``test`` - Build package and run a unittest suite
=================================================
@@ -1079,6 +1191,8 @@ Release Notes/Change History
This is used by the ``easy_install`` command to find possibly-conflicting
"unmanaged" packages when installing the distribution.
+ * Added ``zip_safe`` and ``namespace_packages`` arguments to ``setup()``.
+
* Fixed the swapped ``-d`` and ``-b`` options of ``bdist_egg``.
0.5a8
diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py
index e089cedb..b80dcb8d 100755
--- a/setuptools/command/easy_install.py
+++ b/setuptools/command/easy_install.py
@@ -21,7 +21,7 @@ from distutils.errors import DistutilsArgError, DistutilsOptionError, DistutilsE
from setuptools.archive_util import unpack_archive
from setuptools.package_index import PackageIndex, parse_bdist_wininst
from setuptools.package_index import URL_SCHEME
-from setuptools.command import bdist_egg
+from setuptools.command import bdist_egg, egg_info
from pkg_resources import *
__all__ = [
@@ -697,6 +697,7 @@ PYTHONPATH, or by being added to sys.path by your code.)
def build_and_install(self, setup_script, setup_base, zip_ok):
sys.modules.setdefault('distutils.command.bdist_egg', bdist_egg)
+ sys.modules.setdefault('distutils.command.bdist_egg', egg_info)
args = ['bdist_egg', '--dist-dir']
if self.verbose>2:
@@ -735,7 +736,6 @@ PYTHONPATH, or by being added to sys.path by your code.)
shutil.rmtree(dist_dir)
log.set_verbosity(self.verbose) # restore our log verbosity
-
def update_pth(self,dist):
if self.pth_file is None:
return
diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py
index dca4e2a3..68e2fefb 100755
--- a/setuptools/command/egg_info.py
+++ b/setuptools/command/egg_info.py
@@ -9,7 +9,7 @@ from distutils.errors import *
from distutils import log
from pkg_resources import parse_requirements, safe_name, \
safe_version, yield_lines
-
+from setuptools.dist import iter_distribution_names
class egg_info(Command):
@@ -83,8 +83,8 @@ class egg_info(Command):
def run(self):
# Make the .egg-info directory, then write PKG-INFO and requires.txt
self.mkpath(self.egg_info)
+ log.info("writing %s" % os.path.join(self.egg_info,'PKG-INFO'))
- log.info("writing %s" % os.path.join(self.egg_info,'PKG-INFO'))
if not self.dry_run:
metadata = self.distribution.metadata
metadata.version, oldver = self.egg_version, metadata.version
@@ -96,15 +96,16 @@ class egg_info(Command):
finally:
metadata.name, metadata.version = oldname, oldver
+ self.write_namespace_packages()
self.write_requirements()
self.write_toplevel_names()
+
if os.path.exists(os.path.join(self.egg_info,'depends.txt')):
log.warn(
"WARNING: 'depends.txt' will not be used by setuptools 0.6!\n"
"Use the install_requires/extras_require setup() args instead."
)
-
def write_requirements(self):
dist = self.distribution
if not getattr(dist,'install_requires',None) and \
@@ -120,7 +121,6 @@ class egg_info(Command):
f.write('\n\n[%s]\n%s' % (extra, '\n'.join(yield_lines(reqs))))
f.close()
-
def tagged_version(self):
version = self.distribution.get_version()
if self.tag_build:
@@ -144,16 +144,12 @@ class egg_info(Command):
def write_toplevel_names(self):
- pkgs = dict.fromkeys(self.distribution.packages or ())
- pkgs.update(dict.fromkeys(self.distribution.py_modules or ()))
- for ext in self.distribution.ext_modules or ():
- if isinstance(ext,tuple):
- name,buildinfo = ext
- else:
- name = ext.name
- pkgs[name]=1
- pkgs = dict.fromkeys([k.split('.',1)[0] for k in pkgs])
- toplevel = os.path.join(self.egg_info,"top_level.txt")
+ pkgs = dict.fromkeys(
+ [k.split('.',1)[0]
+ for k in iter_distribution_names(self.distribution)
+ ]
+ )
+ toplevel = os.path.join(self.egg_info, "top_level.txt")
log.info("writing list of top-level names to %s" % toplevel)
if not self.dry_run:
f = open(toplevel, 'wt')
@@ -162,3 +158,48 @@ class egg_info(Command):
f.close()
+
+
+
+
+ def write_namespace_packages(self):
+ nsp = getattr(self.distribution,'namespace_packages',None)
+ if nsp is None:
+ return
+
+ filename = os.path.join(self.egg_info,"namespace_packages.txt")
+
+ if nsp:
+ log.info("writing %s", filename)
+ if not self.dry_run:
+ f = open(filename, 'wt')
+ f.write('\n'.join(nsp))
+ f.write('\n')
+ f.close()
+
+ elif os.path.exists(filename):
+ log.info("deleting %s", filename)
+ if not self.dry_run:
+ os.unlink(filename)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/setuptools/dist.py b/setuptools/dist.py
index 4fef454f..73627752 100644
--- a/setuptools/dist.py
+++ b/setuptools/dist.py
@@ -90,6 +90,8 @@ class Distribution(_Distribution):
self.install_requires = []
self.extras_require = {}
self.dist_files = []
+ self.zip_safe = None
+ self.namespace_packages = None
_Distribution.__init__(self,attrs)
if not have_package_data:
from setuptools.command.build_py import build_py
@@ -100,19 +102,17 @@ class Distribution(_Distribution):
self.cmdclass.setdefault('install_lib',install_lib)
self.cmdclass.setdefault('sdist',sdist)
+ def parse_command_line(self):
+ """Process features after parsing command line options"""
+ result = _Distribution.parse_command_line(self)
+ if self.features:
+ self._finalize_features()
+ return result
-
-
-
-
-
-
-
-
-
-
-
+ def _feature_attrname(self,name):
+ """Convert feature name to corresponding option attribute name"""
+ return 'with_'+name.replace('-','_')
@@ -123,10 +123,8 @@ class Distribution(_Distribution):
def finalize_options(self):
_Distribution.finalize_options(self)
-
if self.features:
self._set_global_opts_from_features()
-
if self.extra_path:
raise DistutilsSetupError(
"The 'extra_path' parameter is not needed when using "
@@ -148,19 +146,21 @@ class Distribution(_Distribution):
"strings or lists of strings containing valid project/version "
"requirement specifiers."
)
-
- def parse_command_line(self):
- """Process features after parsing command line options"""
- result = _Distribution.parse_command_line(self)
- if self.features:
- self._finalize_features()
- return result
-
-
- def _feature_attrname(self,name):
- """Convert feature name to corresponding option attribute name"""
- return 'with_'+name.replace('-','_')
-
+ if self.namespace_packages is not None:
+ try:
+ assert ''.join(self.namespace_packages)!=self.namespace_packages
+ except (TypeError,ValueError,AttributeError,AssertionError):
+ raise DistutilsSetupError(
+ "'namespace_packages' must be a sequence of strings"
+ )
+ for nsp in self.namespace_packages:
+ for name in iter_distribution_names(self):
+ if name.startswith(nsp+'.'): break
+ else:
+ raise DistutilsSetupError(
+ "Distribution contains no modules or packages for " +
+ "namespace package %r" % nsp
+ )
def _set_global_opts_from_features(self):
"""Add --with-X/--without-X options based on optional features"""
@@ -490,6 +490,47 @@ class Distribution(_Distribution):
return d
+def iter_distribution_names(distribution):
+ """Yield all packages, modules, and extensions declared by distribution"""
+
+ for pkg in distribution.packages or ():
+ yield pkg
+
+ for module in distribution.py_modules or ():
+ yield module
+
+ for ext in distribution.ext_modules or ():
+ if isinstance(ext,tuple):
+ name,buildinfo = ext
+ yield name
+ else:
+ yield ext.name
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
class Feature:
"""A subset of the distribution that can be excluded if unneeded/wanted