Skip to content

Commit df0ae04

Browse files
committed
support setup_requires in setup.cfg
1 parent 356539d commit df0ae04

File tree

3 files changed

+111
-19
lines changed

3 files changed

+111
-19
lines changed

setuptools/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,11 @@ def _looks_like_package(path):
109109

110110
find_packages = PackageFinder.find
111111

112-
setup = distutils.core.setup
112+
def setup(**attrs):
113+
if 'parse_config' not in attrs:
114+
attrs['parse_config'] = True
115+
return distutils.core.setup(**attrs)
116+
setup.__doc__ = distutils.core.setup.__doc__
113117

114118
_Command = monkey.get_unpatched(distutils.core.Command)
115119

setuptools/dist.py

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -316,23 +316,19 @@ def __init__(self, attrs=None):
316316
have_package_data = hasattr(self, "package_data")
317317
if not have_package_data:
318318
self.package_data = {}
319-
_attrs_dict = attrs or {}
320-
if 'features' in _attrs_dict or 'require_features' in _attrs_dict:
319+
attrs = attrs or {}
320+
if 'features' in attrs or 'require_features' in attrs:
321321
Feature.warn_deprecated()
322322
self.require_features = []
323323
self.features = {}
324324
self.dist_files = []
325-
self.src_root = attrs and attrs.pop("src_root", None)
325+
self.src_root = attrs.pop("src_root", None)
326326
self.patch_missing_pkg_info(attrs)
327-
self.long_description_content_type = _attrs_dict.get(
327+
self.long_description_content_type = attrs.get(
328328
'long_description_content_type'
329329
)
330330
# Make sure we have any eggs needed to interpret 'attrs'
331-
if attrs is not None:
332-
self.dependency_links = attrs.pop('dependency_links', [])
333-
assert_string_list(self, 'dependency_links', self.dependency_links)
334-
if attrs and 'setup_requires' in attrs:
335-
self.fetch_build_eggs(attrs['setup_requires'])
331+
self._install_setup_requires(attrs)
336332
for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'):
337333
vars(self).setdefault(ep.name, None)
338334
_Distribution.__init__(self, attrs)
@@ -361,6 +357,26 @@ def __init__(self, attrs=None):
361357
)
362358
self._finalize_requires()
363359

360+
def _install_setup_requires(self, attrs):
361+
self.dependency_links = attrs.get('dependency_links')
362+
self.setup_requires = attrs.get('setup_requires')
363+
# If dependency_links/setup_requires were not passed as
364+
# arguments, then we need to honor setup.cfg's options.
365+
if (
366+
attrs.pop('parse_config', False) and
367+
None in (self.dependency_links, self.setup_requires)
368+
):
369+
dist = self.__class__()
370+
dist.parse_config_files(ignore_option_errors=True)
371+
if self.setup_requires is None:
372+
self.setup_requires = dist.setup_requires
373+
if self.dependency_links is None:
374+
self.dependency_links = dist.dependency_links
375+
self.dependency_links = self.dependency_links or []
376+
self.setup_requires = self.setup_requires or []
377+
if self.setup_requires:
378+
self.fetch_build_eggs(self.setup_requires)
379+
364380
def _finalize_requires(self):
365381
"""
366382
Set `metadata.python_requires` and fix environment markers
@@ -427,14 +443,15 @@ def _clean_req(self, req):
427443
req.marker = None
428444
return req
429445

430-
def parse_config_files(self, filenames=None):
446+
def parse_config_files(self, filenames=None, ignore_option_errors=False):
431447
"""Parses configuration files from various levels
432448
and loads configuration.
433449
434450
"""
435451
_Distribution.parse_config_files(self, filenames=filenames)
436452

437-
parse_configuration(self, self.command_options)
453+
parse_configuration(self, self.command_options,
454+
ignore_option_errors=ignore_option_errors)
438455
self._finalize_requires()
439456

440457
def parse_command_line(self):
@@ -497,7 +514,7 @@ def fetch_build_egg(self, req):
497514
"""Fetch an egg needed for building"""
498515
from setuptools.command.easy_install import easy_install
499516
dist = self.__class__({'script_args': ['easy_install']})
500-
dist.parse_config_files()
517+
dist.parse_config_files(ignore_option_errors=True)
501518
opts = dist.get_option_dict('easy_install')
502519
keep = (
503520
'find_links', 'site_dirs', 'index_url', 'optimize',

setuptools/tests/test_easy_install.py

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,15 @@ def create_sdist():
381381
"""))])
382382
yield dist_path
383383

384-
def test_setup_requires_overrides_version_conflict(self):
384+
use_setup_cfg = (
385+
(),
386+
('dependency_links',),
387+
('setup_requires',),
388+
('dependency_links', 'setup_requires'),
389+
)
390+
391+
@pytest.mark.parametrize('use_setup_cfg', use_setup_cfg)
392+
def test_setup_requires_overrides_version_conflict(self, use_setup_cfg):
385393
"""
386394
Regression test for distribution issue 323:
387395
https://bitbucket.org/tarek/distribute/issues/323
@@ -397,7 +405,7 @@ def test_setup_requires_overrides_version_conflict(self):
397405

398406
with contexts.save_pkg_resources_state():
399407
with contexts.tempdir() as temp_dir:
400-
test_pkg = create_setup_requires_package(temp_dir)
408+
test_pkg = create_setup_requires_package(temp_dir, use_setup_cfg=use_setup_cfg)
401409
test_setup_py = os.path.join(test_pkg, 'setup.py')
402410
with contexts.quiet() as (stdout, stderr):
403411
# Don't even need to install the package, just
@@ -408,7 +416,8 @@ def test_setup_requires_overrides_version_conflict(self):
408416
assert len(lines) > 0
409417
assert lines[-1].strip() == 'test_pkg'
410418

411-
def test_setup_requires_override_nspkg(self):
419+
@pytest.mark.parametrize('use_setup_cfg', use_setup_cfg)
420+
def test_setup_requires_override_nspkg(self, use_setup_cfg):
412421
"""
413422
Like ``test_setup_requires_overrides_version_conflict`` but where the
414423
``setup_requires`` package is part of a namespace package that has
@@ -446,7 +455,8 @@ def test_setup_requires_override_nspkg(self):
446455
""")
447456

448457
test_pkg = create_setup_requires_package(
449-
temp_dir, 'foo.bar', '0.2', make_nspkg_sdist, template)
458+
temp_dir, 'foo.bar', '0.2', make_nspkg_sdist, template,
459+
use_setup_cfg=use_setup_cfg)
450460

451461
test_setup_py = os.path.join(test_pkg, 'setup.py')
452462

@@ -464,6 +474,38 @@ def test_setup_requires_override_nspkg(self):
464474
assert len(lines) > 0
465475
assert lines[-1].strip() == 'test_pkg'
466476

477+
@pytest.mark.parametrize('use_setup_cfg', use_setup_cfg)
478+
def test_setup_requires_with_attr_version(self, use_setup_cfg):
479+
def make_dependency_sdist(dist_path, distname, version):
480+
make_sdist(dist_path, [
481+
('setup.py',
482+
DALS("""
483+
import setuptools
484+
setuptools.setup(
485+
name={name!r},
486+
version={version!r},
487+
py_modules=[{name!r}],
488+
)
489+
""".format(name=distname, version=version))),
490+
(distname + '.py',
491+
DALS("""
492+
version = 42
493+
"""
494+
))])
495+
with contexts.save_pkg_resources_state():
496+
with contexts.tempdir() as temp_dir:
497+
test_pkg = create_setup_requires_package(
498+
temp_dir, setup_attrs=dict(version='attr: foobar.version'),
499+
make_package=make_dependency_sdist,
500+
use_setup_cfg=use_setup_cfg+('version',),
501+
)
502+
test_setup_py = os.path.join(test_pkg, 'setup.py')
503+
with contexts.quiet() as (stdout, stderr):
504+
run_setup(test_setup_py, ['--version'])
505+
lines = stdout.readlines()
506+
assert len(lines) > 0
507+
assert lines[-1].strip() == '42'
508+
467509

468510
def make_trivial_sdist(dist_path, distname, version):
469511
"""
@@ -532,7 +574,8 @@ def make_sdist(dist_path, files):
532574

533575
def create_setup_requires_package(path, distname='foobar', version='0.1',
534576
make_package=make_trivial_sdist,
535-
setup_py_template=None):
577+
setup_py_template=None, setup_attrs={},
578+
use_setup_cfg=()):
536579
"""Creates a source tree under path for a trivial test package that has a
537580
single requirement in setup_requires--a tarball for that requirement is
538581
also created and added to the dependency_links argument.
@@ -547,11 +590,39 @@ def create_setup_requires_package(path, distname='foobar', version='0.1',
547590
'setup_requires': ['%s==%s' % (distname, version)],
548591
'dependency_links': [os.path.abspath(path)]
549592
}
593+
test_setup_attrs.update(setup_attrs)
550594

551595
test_pkg = os.path.join(path, 'test_pkg')
552-
test_setup_py = os.path.join(test_pkg, 'setup.py')
553596
os.mkdir(test_pkg)
554597

598+
if use_setup_cfg:
599+
test_setup_cfg = os.path.join(test_pkg, 'setup.cfg')
600+
options = []
601+
metadata = []
602+
for name in use_setup_cfg:
603+
value = test_setup_attrs.pop(name)
604+
if name in 'name version'.split():
605+
section = metadata
606+
else:
607+
section = options
608+
if isinstance(value, (tuple, list)):
609+
value = ';'.join(value)
610+
section.append('%s: %s' % (name, value))
611+
with open(test_setup_cfg, 'w') as f:
612+
f.write(DALS(
613+
"""
614+
[metadata]
615+
{metadata}
616+
[options]
617+
{options}
618+
"""
619+
).format(
620+
options='\n'.join(options),
621+
metadata='\n'.join(metadata),
622+
))
623+
624+
test_setup_py = os.path.join(test_pkg, 'setup.py')
625+
555626
if setup_py_template is None:
556627
setup_py_template = DALS("""\
557628
import setuptools

0 commit comments

Comments
 (0)