summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarsten Klein <trancesilken@gmail.com>2018-08-17 14:58:35 +0200
committerPaul Ganssle <pganssle@users.noreply.github.com>2018-08-17 08:58:35 -0400
commit0254a2fda8e8bd4f289d01e2179191e936517f04 (patch)
tree6007c5bc0b659c4d69ffbcc8df917f7a0c747cdf
parentbbf99b7e599766e15dc56f58f68fe6c32c972faf (diff)
downloadpython-setuptools-git-0254a2fda8e8bd4f289d01e2179191e936517f04.tar.gz
Rename find_namepaces_ns to find_namespace_packages (#1423)
* fix #1419 PEP420: add find_namespace: directive * fix #1419 PEP420: add find_namespace: directive to documentation * fix #1419 PEP420: add tests * fix #1419 PEP420: clean up code * fix #1419 PEP420: fix typo in documentation * fix #1419 PEP420: fix typo in documentation * fix #1419 PEP420: clean up code * fix #1419 PEP420: add changelog entry * fixup! fix #1419 PEP420: add tests * fix #1419 PEP420: cleanup code refactor markers * #1420: Rename find_namespace_ns to find_namespace_packages * #1420: update changelog entry
-rw-r--r--changelog.d/1312.change.rst2
-rw-r--r--changelog.d/1420.change.rst1
-rw-r--r--docs/setuptools.txt35
-rw-r--r--setuptools/__init__.py4
-rw-r--r--setuptools/config.py16
-rw-r--r--setuptools/tests/__init__.py12
-rw-r--r--setuptools/tests/test_config.py64
-rw-r--r--setuptools/tests/test_find_packages.py16
8 files changed, 115 insertions, 35 deletions
diff --git a/changelog.d/1312.change.rst b/changelog.d/1312.change.rst
index 9314f9e1..e2542f98 100644
--- a/changelog.d/1312.change.rst
+++ b/changelog.d/1312.change.rst
@@ -1 +1 @@
-Introduce find_packages_ns() to find PEP 420 namespace packages.
+Introduce find_namespace_packages() to find PEP 420 namespace packages.
diff --git a/changelog.d/1420.change.rst b/changelog.d/1420.change.rst
new file mode 100644
index 00000000..6967adcc
--- /dev/null
+++ b/changelog.d/1420.change.rst
@@ -0,0 +1 @@
+Add find_namespace: directive to config parser
diff --git a/docs/setuptools.txt b/docs/setuptools.txt
index 0660e14d..89f45bd8 100644
--- a/docs/setuptools.txt
+++ b/docs/setuptools.txt
@@ -57,7 +57,7 @@ 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
+* Full support for PEP 420 via ``find_namespace_packages()``, which is also backwards
compatible to the existing ``find_packages()`` for Python >= 3.3.
.. contents:: **Table of Contents**
@@ -462,18 +462,18 @@ 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
+``find_namespace_packages()``
+-----------------------------
+In Python 3.3+, ``setuptools`` also provides the ``find_namespace_packages`` 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``::
+packages. Here is a minimal setup script using ``find_namespace_packages``::
- from setuptools import setup, find_packages_ns
+ from setuptools import setup, find_namespace_packages
setup(
name="HelloWorld",
version="0.1",
- packages=find_packages_ns(),
+ packages=find_namespace_packages(),
)
@@ -490,16 +490,16 @@ namespace package is quite lenient, so for a project organized like so::
└── tests
└── test_mod1.py
-A naive ``find_packages_ns()`` would install both ``namespace.mypackage`` and a
+A naive ``find_namespace_packages()`` 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
+ from setuptools import setup, find_namespace_packages
setup(
name="namespace.mypackage",
version="0.1",
- packages=find_packages_ns(include=['namespace.*'])
+ packages=find_namespace_packages(include=['namespace.*'])
)
Another option is to use the "src" layout, where all package code is placed in
@@ -520,7 +520,7 @@ 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'))
+ packages=find_namespace_packages(where='src'))
.. _PEP 420: https://www.python.org/dev/peps/pep-0420/
@@ -2389,8 +2389,8 @@ Metadata and options are set in the config sections of the same name.
* In some cases, complex values can be provided in dedicated subsections for
clarity.
-* Some keys allow ``file:``, ``attr:``, and ``find:`` directives in order to
- cover common usecases.
+* Some keys allow ``file:``, ``attr:``, and ``find:`` and ``find_namespace:`` directives in
+ order to cover common usecases.
* Unknown keys are ignored.
@@ -2479,7 +2479,7 @@ eager_resources list-comma
dependency_links list-comma
tests_require list-semi
include_package_data bool
-packages find:, list-comma
+packages find:, find_namespace:, list-comma
package_dir dict
package_data section
exclude_package_data section
@@ -2489,10 +2489,13 @@ py_modules list-comma
.. note::
- **packages** - The ``find:`` directive can be further configured
+ **packages** - The ``find:`` and ``find_namespace:`` directive can be further configured
in a dedicated subsection ``options.packages.find``. This subsection
- accepts the same keys as the `setuptools.find` function:
+ accepts the same keys as the `setuptools.find_packages` and the
+ `setuptools.find_namespace_packages` function:
``where``, ``include``, and ``exclude``.
+
+ **find_namespace directive** - The ``find_namespace:`` directive is supported since Python >=3.3.
Configuration API
diff --git a/setuptools/__init__.py b/setuptools/__init__.py
index e705f0d1..54309b57 100644
--- a/setuptools/__init__.py
+++ b/setuptools/__init__.py
@@ -26,7 +26,7 @@ __all__ = [
]
if PY3:
- __all__.append('find_packages_ns')
+ __all__.append('find_namespace_packages')
__version__ = setuptools.version.__version__
@@ -118,7 +118,7 @@ class PEP420PackageFinder(PackageFinder):
find_packages = PackageFinder.find
if PY3:
- find_packages_ns = PEP420PackageFinder.find
+ find_namespace_packages = PEP420PackageFinder.find
def _install_setup_requires(attrs):
diff --git a/setuptools/config.py b/setuptools/config.py
index 5f908cf1..0da3dbc9 100644
--- a/setuptools/config.py
+++ b/setuptools/config.py
@@ -8,7 +8,7 @@ from importlib import import_module
from distutils.errors import DistutilsOptionError, DistutilsFileError
from setuptools.extern.packaging.version import LegacyVersion, parse
-from setuptools.extern.six import string_types
+from setuptools.extern.six import string_types, PY3
__metaclass__ = type
@@ -515,16 +515,24 @@ class ConfigOptionsHandler(ConfigHandler):
:param value:
:rtype: list
"""
- find_directive = 'find:'
+ find_directives = ['find:', 'find_namespace:']
+ trimmed_value = value.strip()
- if not value.startswith(find_directive):
+ if not trimmed_value in find_directives:
return self._parse_list(value)
+ findns = trimmed_value == find_directives[1]
+ if findns and not PY3:
+ raise DistutilsOptionError('find_namespace: directive is unsupported on Python < 3.3')
+
# Read function arguments from a dedicated section.
find_kwargs = self.parse_section_packages__find(
self.sections.get('packages.find', {}))
- from setuptools import find_packages
+ if findns:
+ from setuptools import find_namespace_packages as find_packages
+ else:
+ from setuptools import find_packages
return find_packages(**find_kwargs)
diff --git a/setuptools/tests/__init__.py b/setuptools/tests/__init__.py
index 54dd7d2b..5f4a1c29 100644
--- a/setuptools/tests/__init__.py
+++ b/setuptools/tests/__init__.py
@@ -2,5 +2,17 @@ import locale
import pytest
+from setuptools.extern.six import PY2, PY3
+
+
+__all__ = [
+ 'fail_on_ascii', 'py2_only', 'py3_only'
+]
+
+
is_ascii = locale.getpreferredencoding() == 'ANSI_X3.4-1968'
fail_on_ascii = pytest.mark.xfail(is_ascii, reason="Test fails in this locale")
+
+
+py2_only = pytest.mark.skipif(not PY2, reason="Test runs on Python 2 only")
+py3_only = pytest.mark.skipif(not PY3, reason="Test runs on Python 3 only")
diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py
index 19b37633..acf22154 100644
--- a/setuptools/tests/test_config.py
+++ b/setuptools/tests/test_config.py
@@ -4,18 +4,20 @@ from distutils.errors import DistutilsOptionError, DistutilsFileError
from mock import patch
from setuptools.dist import Distribution, _Distribution
from setuptools.config import ConfigHandler, read_configuration
-
+from . import py2_only, py3_only
class ErrConfigHandler(ConfigHandler):
"""Erroneous handler. Fails to implement required methods."""
-def make_package_dir(name, base_dir):
+def make_package_dir(name, base_dir, ns=False):
dir_package = base_dir
for dir_name in name.split('/'):
dir_package = dir_package.mkdir(dir_name)
- init_file = dir_package.join('__init__.py')
- init_file.write('')
+ init_file = None
+ if not ns:
+ init_file = dir_package.join('__init__.py')
+ init_file.write('')
return dir_package, init_file
@@ -596,6 +598,60 @@ class TestOptions:
assert set(dist.packages) == set(
['fake_package', 'fake_package.sub_two'])
+ @py2_only
+ def test_find_namespace_directive_fails_on_py2(self, tmpdir):
+ dir_package, config = fake_env(
+ tmpdir,
+ '[options]\n'
+ 'packages = find_namespace:\n'
+ )
+
+ with pytest.raises(DistutilsOptionError):
+ with get_dist(tmpdir) as dist:
+ dist.parse_config_files()
+
+ @py3_only
+ def test_find_namespace_directive(self, tmpdir):
+ dir_package, config = fake_env(
+ tmpdir,
+ '[options]\n'
+ 'packages = find_namespace:\n'
+ )
+
+ dir_sub_one, _ = make_package_dir('sub_one', dir_package)
+ dir_sub_two, _ = make_package_dir('sub_two', dir_package, ns=True)
+
+ with get_dist(tmpdir) as dist:
+ assert set(dist.packages) == {
+ 'fake_package', 'fake_package.sub_two', 'fake_package.sub_one'
+ }
+
+ config.write(
+ '[options]\n'
+ 'packages = find_namespace:\n'
+ '\n'
+ '[options.packages.find]\n'
+ 'where = .\n'
+ 'include =\n'
+ ' fake_package.sub_one\n'
+ ' two\n'
+ )
+ with get_dist(tmpdir) as dist:
+ assert dist.packages == ['fake_package.sub_one']
+
+ config.write(
+ '[options]\n'
+ 'packages = find_namespace:\n'
+ '\n'
+ '[options.packages.find]\n'
+ 'exclude =\n'
+ ' fake_package.sub_one\n'
+ )
+ with get_dist(tmpdir) as dist:
+ assert set(dist.packages) == {
+ 'fake_package', 'fake_package.sub_two'
+ }
+
def test_extras_require(self, tmpdir):
fake_env(
tmpdir,
diff --git a/setuptools/tests/test_find_packages.py b/setuptools/tests/test_find_packages.py
index 02ae5a94..b08f91c7 100644
--- a/setuptools/tests/test_find_packages.py
+++ b/setuptools/tests/test_find_packages.py
@@ -7,12 +7,12 @@ import platform
import pytest
+from . import py3_only
+
from setuptools.extern.six import PY3
from setuptools import find_packages
-
-py3_only = pytest.mark.xfail(not PY3, reason="Test runs on Python 3 only")
if PY3:
- from setuptools import find_packages_ns
+ from setuptools import find_namespace_packages
# modeled after CPython's test.support.can_symlink
@@ -156,26 +156,26 @@ class TestFindPackages:
@py3_only
def test_pep420_ns_package(self):
- packages = find_packages_ns(
+ packages = find_namespace_packages(
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_packages_ns(
+ packages = find_namespace_packages(
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_packages_ns(self.dist_dir)
+ packages = find_namespace_packages(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_packages_ns(
+ packages = find_namespace_packages(
self.dist_dir, exclude=['docs', 'pkg.subpkg.assets'])
self._assert_packages(packages, ['pkg', 'pkg.nspkg', 'pkg.subpkg'])
@@ -183,6 +183,6 @@ class TestFindPackages:
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_packages_ns(self.dist_dir)
+ packages = find_namespace_packages(self.dist_dir)
self._assert_packages(packages, ['pkg', 'pkg.nspkg', 'pkg.subpkg'])