Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add findns: directive to config #1420

Closed
wants to merge 10 commits into from
1 change: 1 addition & 0 deletions changelog.d/1420.change.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add find_namespace: directive to config parser
13 changes: 8 additions & 5 deletions docs/setuptools.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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
Expand All @@ -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_packages_ns` function:
``where``, ``include``, and ``exclude``.

**find_namespace directive** - The ``find_namespace:`` directive is supported since Python >=3.3.


Configuration API
Expand Down
16 changes: 12 additions & 4 deletions setuptools/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

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
Expand Down Expand Up @@ -515,16 +515,24 @@ def _parse_packages(self, value):
: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_packages_ns as find_packages
else:
from setuptools import find_packages

return find_packages(**find_kwargs)

Expand Down
12 changes: 12 additions & 0 deletions setuptools/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,17 @@

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")
64 changes: 60 additions & 4 deletions setuptools/tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@
from mock import patch
from setuptools.dist import Distribution, _Distribution
from setuptools.config import ConfigHandler, read_configuration

from setuptools.tests 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


Expand Down Expand Up @@ -596,6 +598,60 @@ def test_find_directive(self, tmpdir):
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,
Expand Down
4 changes: 2 additions & 2 deletions setuptools/tests/test_find_packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@

import pytest

from setuptools.tests 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

Expand Down