diff options
author | Carsten Klein <trancesilken@gmail.com> | 2018-08-17 14:58:35 +0200 |
---|---|---|
committer | Paul Ganssle <pganssle@users.noreply.github.com> | 2018-08-17 08:58:35 -0400 |
commit | 0254a2fda8e8bd4f289d01e2179191e936517f04 (patch) | |
tree | 6007c5bc0b659c4d69ffbcc8df917f7a0c747cdf | |
parent | bbf99b7e599766e15dc56f58f68fe6c32c972faf (diff) | |
download | python-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.rst | 2 | ||||
-rw-r--r-- | changelog.d/1420.change.rst | 1 | ||||
-rw-r--r-- | docs/setuptools.txt | 35 | ||||
-rw-r--r-- | setuptools/__init__.py | 4 | ||||
-rw-r--r-- | setuptools/config.py | 16 | ||||
-rw-r--r-- | setuptools/tests/__init__.py | 12 | ||||
-rw-r--r-- | setuptools/tests/test_config.py | 64 | ||||
-rw-r--r-- | setuptools/tests/test_find_packages.py | 16 |
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']) |