summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAarni Koskela <akx@iki.fi>2022-04-06 16:06:08 +0300
committerAarni Koskela <akx@iki.fi>2022-06-14 14:51:56 +0300
commit40fbbf2e7062f8482161576db8366cd4a7caaa93 (patch)
tree3961cece96fb2d29a8c83a017a11d70166dcbad6
parent78cb747d66bda1a6f6649e82690aaf5083a89d69 (diff)
downloadpython-setuptools-git-40fbbf2e7062f8482161576db8366cd4a7caaa93.tar.gz
Allow `file:` for `requires` statements in setup.cfg
Refs #1951
-rw-r--r--changelog.d/3253.change.rst1
-rw-r--r--docs/userguide/declarative_config.rst15
-rw-r--r--setuptools/config/setupcfg.py30
-rw-r--r--setuptools/tests/config/test_setupcfg.py18
4 files changed, 51 insertions, 13 deletions
diff --git a/changelog.d/3253.change.rst b/changelog.d/3253.change.rst
new file mode 100644
index 00000000..0c29e2a6
--- /dev/null
+++ b/changelog.d/3253.change.rst
@@ -0,0 +1 @@
+Enabled using ``file:`` for requirements in setup.cfg -- by :user:`akx`
diff --git a/docs/userguide/declarative_config.rst b/docs/userguide/declarative_config.rst
index aa8bc7ea..a57bb607 100644
--- a/docs/userguide/declarative_config.rst
+++ b/docs/userguide/declarative_config.rst
@@ -211,13 +211,13 @@ obsoletes list-comma
Options
-------
-======================= =================================== =============== =========
+======================= =================================== =============== ====================
Key Type Minimum Version Notes
-======================= =================================== =============== =========
+======================= =================================== =============== ====================
zip_safe bool
setup_requires list-semi 36.7.0
-install_requires list-semi
-extras_require section [#opt-2]_
+install_requires file:, list-semi [#opt-6]_
+extras_require file:, section [#opt-2]_, [#opt-6]_
python_requires str 34.4.0
entry_points file:, section 51.0.0
scripts list-comma
@@ -232,7 +232,7 @@ exclude_package_data section
namespace_packages list-comma [#opt-5]_
py_modules list-comma 34.4.0
data_files section 40.6.0 [#opt-4]_
-======================= =================================== =============== =========
+======================= =================================== =============== ====================
**Notes**:
@@ -266,6 +266,11 @@ data_files section 40.6.0 [#
namespaces (:pep:`420`). Check :doc:`the Python Packaging User Guide
<PyPUG:guides/packaging-namespace-packages>` for more information.
+.. [#opt-6] ``file:`` directives for reading requirements are supported since version 63.0.
+ The format for the file is basically the same as for a ``requirements.txt`` file.
+ Library developers should avoid tightly pinning their dependencies to a specific
+ version (e.g. via a "locked" requirements file).
+
Compatibility with other tools
==============================
diff --git a/setuptools/config/setupcfg.py b/setuptools/config/setupcfg.py
index b2d5c346..caf7694a 100644
--- a/setuptools/config/setupcfg.py
+++ b/setuptools/config/setupcfg.py
@@ -568,15 +568,27 @@ class ConfigOptionsHandler(ConfigHandler["Distribution"]):
self.root_dir = target_obj.src_root
self.package_dir: Dict[str, str] = {} # To be filled by `find_packages`
+ @classmethod
+ def _parse_list_semicolon(cls, value):
+ return cls._parse_list(value, separator=';')
+
+ def _parse_file_in_root(self, value):
+ return self._parse_file(value, root_dir=self.root_dir)
+
+ def _parse_requirements_list(self, value):
+ # Parse a requirements list, either by reading in a `file:`, or a list.
+ parsed = self._parse_list_semicolon(self._parse_file_in_root(value))
+ # Filter it to only include lines that are not comments. `parse_list`
+ # will have stripped each line and filtered out empties.
+ return [line for line in parsed if not line.startswith("#")]
+
@property
def parsers(self):
"""Metadata item name to parser function mapping."""
parse_list = self._parse_list
- parse_list_semicolon = partial(self._parse_list, separator=';')
parse_bool = self._parse_bool
parse_dict = self._parse_dict
parse_cmdclass = self._parse_cmdclass
- parse_file = partial(self._parse_file, root_dir=self.root_dir)
return {
'zip_safe': parse_bool,
@@ -591,11 +603,11 @@ class ConfigOptionsHandler(ConfigHandler["Distribution"]):
"consider using implicit namespaces instead (PEP 420).",
SetuptoolsDeprecationWarning,
),
- 'install_requires': parse_list_semicolon,
- 'setup_requires': parse_list_semicolon,
- 'tests_require': parse_list_semicolon,
+ 'install_requires': self._parse_requirements_list,
+ 'setup_requires': self._parse_list_semicolon,
+ 'tests_require': self._parse_list_semicolon,
'packages': self._parse_packages,
- 'entry_points': parse_file,
+ 'entry_points': self._parse_file_in_root,
'py_modules': parse_list,
'python_requires': SpecifierSet,
'cmdclass': parse_cmdclass,
@@ -682,8 +694,10 @@ class ConfigOptionsHandler(ConfigHandler["Distribution"]):
:param dict section_options:
"""
- parse_list = partial(self._parse_list, separator=';')
- parsed = self._parse_section_to_dict(section_options, parse_list)
+ parsed = self._parse_section_to_dict(
+ section_options,
+ self._parse_requirements_list,
+ )
self['extras_require'] = parsed
def parse_section_data_files(self, section_options):
diff --git a/setuptools/tests/config/test_setupcfg.py b/setuptools/tests/config/test_setupcfg.py
index 904b1ef8..b2563a10 100644
--- a/setuptools/tests/config/test_setupcfg.py
+++ b/setuptools/tests/config/test_setupcfg.py
@@ -884,6 +884,24 @@ class TestOptions:
assert cmdclass.__module__ == "custom_build"
assert module_path.samefile(inspect.getfile(cmdclass))
+ def test_requirements_file(self, tmpdir):
+ fake_env(
+ tmpdir,
+ DALS("""
+ [options]
+ install_requires = file:requirements.txt
+ [options.extras_require]
+ colors = file:requirements-extra.txt
+ """)
+ )
+
+ tmpdir.join('requirements.txt').write('\ndocutils>=0.3\n\n')
+ tmpdir.join('requirements-extra.txt').write('colorama')
+
+ with get_dist(tmpdir) as dist:
+ assert dist.install_requires == ['docutils>=0.3']
+ assert dist.extras_require == {'colors': ['colorama']}
+
saved_dist_init = _Distribution.__init__