diff options
author | Paul Ganssle <pganssle@users.noreply.github.com> | 2018-07-11 09:16:49 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-07-11 09:16:49 -0400 |
commit | 89155abb4222cf5a9dc81120e5c71e26b5af68f9 (patch) | |
tree | 8f0559e943fafedd6720d596ac8ebdecdcf8bcc6 | |
parent | e50d77efb25d9db04a87ceea483d6b74fc58cd91 (diff) | |
parent | a5797d2c468c1d7005ea36b77ce00941e7c8b251 (diff) | |
download | python-setuptools-git-89155abb4222cf5a9dc81120e5c71e26b5af68f9.tar.gz |
Merge pull request #1312 from coldrye-collaboration/gh-97
fix #97 PEP420: find_packages()
-rw-r--r-- | changelog.d/1312.change.rst | 1 | ||||
-rw-r--r-- | docs/setuptools.txt | 64 | ||||
-rw-r--r-- | setuptools/__init__.py | 11 | ||||
-rw-r--r-- | setuptools/tests/test_find_packages.py | 26 |
4 files changed, 91 insertions, 11 deletions
diff --git a/changelog.d/1312.change.rst b/changelog.d/1312.change.rst new file mode 100644 index 00000000..9314f9e1 --- /dev/null +++ b/changelog.d/1312.change.rst @@ -0,0 +1 @@ +Introduce find_packages_ns() to find PEP 420 namespace packages. diff --git a/docs/setuptools.txt b/docs/setuptools.txt index 511a9898..0660e14d 100644 --- a/docs/setuptools.txt +++ b/docs/setuptools.txt @@ -57,6 +57,9 @@ Feature Highlights: * Create extensible applications and frameworks that automatically discover extensions, using simple "entry points" declared in a project's setup script. +* Full support for PEP 420 via ``find_packages_ns()``, which is also backwards + compatible to the existing ``find_packages()`` for Python >= 3.3. + .. contents:: **Table of Contents** .. _ez_setup.py: `bootstrap module`_ @@ -459,6 +462,67 @@ argument in your setup script. Especially since it frees you from having to remember to modify your setup script whenever your project grows additional top-level packages or subpackages. +``find_packages_ns()`` +---------------------- +In Python 3.3+, ``setuptools`` also provides the ``find_packages_ns`` variant +of ``find_packages``, which has the same function signature as +``find_packages``, but works with `PEP 420`_ compliant implicit namespace +packages. Here is a minimal setup script using ``find_packages_ns``:: + + from setuptools import setup, find_packages_ns + setup( + name="HelloWorld", + version="0.1", + packages=find_packages_ns(), + ) + + +Keep in mind that according to PEP 420, you may have to either re-organize your +codebase a bit or define a few exclusions, as the definition of an implicit +namespace package is quite lenient, so for a project organized like so:: + + + ├── namespace + │ └── mypackage + │ ├── __init__.py + │ └── mod1.py + ├── setup.py + └── tests + └── test_mod1.py + +A naive ``find_packages_ns()`` would install both ``namespace.mypackage`` and a +top-level package called ``tests``! One way to avoid this problem is to use the +``include`` keyword to whitelist the packages to include, like so:: + + from setuptools import setup, find_packages_ns + + setup( + name="namespace.mypackage", + version="0.1", + packages=find_packages_ns(include=['namespace.*']) + ) + +Another option is to use the "src" layout, where all package code is placed in +the ``src`` directory, like so:: + + + ├── setup.py + ├── src + │ └── namespace + │ └── mypackage + │ ├── __init__.py + │ └── mod1.py + └── tests + └── test_mod1.py + +With this layout, the package directory is specified as ``src``, as such:: + + setup(name="namespace.mypackage", + version="0.1", + package_dir={'': 'src'}, + packages=find_packages_ns(where='src')) + +.. _PEP 420: https://www.python.org/dev/peps/pep-0420/ Automatic Script Creation ========================= diff --git a/setuptools/__init__.py b/setuptools/__init__.py index ce55ec35..e705f0d1 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -1,12 +1,14 @@ """Extensions to the 'distutils' for large or complex distributions""" import os +import sys import functools import distutils.core import distutils.filelist from distutils.util import convert_path from fnmatch import fnmatchcase +from setuptools.extern.six import PY3 from setuptools.extern.six.moves import filter, map import setuptools.version @@ -17,11 +19,15 @@ from . import monkey __metaclass__ = type + __all__ = [ 'setup', 'Distribution', 'Feature', 'Command', 'Extension', 'Require', - 'find_packages', + 'find_packages' ] +if PY3: + __all__.append('find_packages_ns') + __version__ = setuptools.version.__version__ bootstrap_install_from = None @@ -111,6 +117,9 @@ class PEP420PackageFinder(PackageFinder): find_packages = PackageFinder.find +if PY3: + find_packages_ns = PEP420PackageFinder.find + def _install_setup_requires(attrs): # Note: do not use `setuptools.Distribution` directly, as diff --git a/setuptools/tests/test_find_packages.py b/setuptools/tests/test_find_packages.py index a6023de9..02ae5a94 100644 --- a/setuptools/tests/test_find_packages.py +++ b/setuptools/tests/test_find_packages.py @@ -7,14 +7,15 @@ import platform import pytest -import setuptools +from setuptools.extern.six import PY3 from setuptools import find_packages -find_420_packages = setuptools.PEP420PackageFinder.find +py3_only = pytest.mark.xfail(not PY3, reason="Test runs on Python 3 only") +if PY3: + from setuptools import find_packages_ns # modeled after CPython's test.support.can_symlink - def can_symlink(): TESTFN = tempfile.mktemp() symlink_path = TESTFN + "can_symlink" @@ -153,30 +154,35 @@ class TestFindPackages: def _assert_packages(self, actual, expected): assert set(actual) == set(expected) + @py3_only def test_pep420_ns_package(self): - packages = find_420_packages( + packages = find_packages_ns( self.dist_dir, include=['pkg*'], exclude=['pkg.subpkg.assets']) self._assert_packages(packages, ['pkg', 'pkg.nspkg', 'pkg.subpkg']) + @py3_only def test_pep420_ns_package_no_includes(self): - packages = find_420_packages( + packages = find_packages_ns( self.dist_dir, exclude=['pkg.subpkg.assets']) self._assert_packages(packages, ['docs', 'pkg', 'pkg.nspkg', 'pkg.subpkg']) + @py3_only def test_pep420_ns_package_no_includes_or_excludes(self): - packages = find_420_packages(self.dist_dir) - expected = [ - 'docs', 'pkg', 'pkg.nspkg', 'pkg.subpkg', 'pkg.subpkg.assets'] + packages = find_packages_ns(self.dist_dir) + expected = ['docs', 'pkg', 'pkg.nspkg', 'pkg.subpkg', 'pkg.subpkg.assets'] self._assert_packages(packages, expected) + @py3_only def test_regular_package_with_nested_pep420_ns_packages(self): self._touch('__init__.py', self.pkg_dir) - packages = find_420_packages( + packages = find_packages_ns( self.dist_dir, exclude=['docs', 'pkg.subpkg.assets']) self._assert_packages(packages, ['pkg', 'pkg.nspkg', 'pkg.subpkg']) + @py3_only def test_pep420_ns_package_no_non_package_dirs(self): shutil.rmtree(self.docs_dir) shutil.rmtree(os.path.join(self.dist_dir, 'pkg/subpkg/assets')) - packages = find_420_packages(self.dist_dir) + packages = find_packages_ns(self.dist_dir) self._assert_packages(packages, ['pkg', 'pkg.nspkg', 'pkg.subpkg']) + |