summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason R. Coombs <jaraco@jaraco.com>2020-09-23 17:23:06 -0400
committerJason R. Coombs <jaraco@jaraco.com>2020-09-23 17:24:11 -0400
commitf852d1598a7814f5f4f6e8bf26d3d60cffe1798a (patch)
tree983e42e98edb42edd86fd42d54e144a561f4a3c4
parent412d2e17aebedfdff443dafa0d3584dd22e0b9ee (diff)
parent52c394c1e87b863aec92949e4b494ab01a7cd234 (diff)
downloadpython-setuptools-git-f852d1598a7814f5f4f6e8bf26d3d60cffe1798a.tar.gz
Merge commit '52c394c' into feature/2093-docs-revamp
-rw-r--r--.bumpversion.cfg2
-rw-r--r--CHANGES.rst9
-rw-r--r--changelog.d/1700.change.rst1
-rw-r--r--changelog.d/1753.change.rst4
-rw-r--r--docs/developer-guide.txt2
-rw-r--r--docs/userguide/commands.txt4
-rw-r--r--setup.cfg2
-rw-r--r--setuptools/config.py92
-rw-r--r--setuptools/tests/test_config.py34
9 files changed, 85 insertions, 65 deletions
diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index 4ee92185..72d02f2b 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -1,5 +1,5 @@
[bumpversion]
-current_version = 46.3.1
+current_version = 46.4.0
commit = True
tag = True
diff --git a/CHANGES.rst b/CHANGES.rst
index fd3c16ba..ea667028 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,3 +1,12 @@
+v46.4.0
+-------
+
+* #1753: ``attr:`` now extracts variables through rudimentary examination of the AST,
+ thereby supporting modules with third-party imports. If examining the AST
+ fails to find the variable, ``attr:`` falls back to the old behavior of
+ importing the module. Works on Python 3 only.
+
+
v46.3.1
-------
diff --git a/changelog.d/1700.change.rst b/changelog.d/1700.change.rst
new file mode 100644
index 00000000..f66046a2
--- /dev/null
+++ b/changelog.d/1700.change.rst
@@ -0,0 +1 @@
+Document all supported keywords by migrating the ones from distutils.
diff --git a/changelog.d/1753.change.rst b/changelog.d/1753.change.rst
deleted file mode 100644
index c8b68026..00000000
--- a/changelog.d/1753.change.rst
+++ /dev/null
@@ -1,4 +0,0 @@
-``attr:`` now extracts variables through rudimentary examination of the AST,
-thereby supporting modules with third-party imports. If examining the AST
-fails to find the variable, ``attr:`` falls back to the old behavior of
-importing the module.
diff --git a/docs/developer-guide.txt b/docs/developer-guide.txt
index 0b4ae4d4..e6171e4e 100644
--- a/docs/developer-guide.txt
+++ b/docs/developer-guide.txt
@@ -139,7 +139,7 @@ Vendored Dependencies
---------------------
Setuptools has some dependencies, but due to `bootstrapping issues
-<https://github.com/pypa/setuptools/issues/980>`, those dependencies
+<https://github.com/pypa/setuptools/issues/980>`_, those dependencies
cannot be declared as they won't be resolved soon enough to build
setuptools from source. Eventually, this limitation may be lifted as
PEP 517/518 reach ubiquitous adoption, but for now, Setuptools
diff --git a/docs/userguide/commands.txt b/docs/userguide/commands.txt
index 86048416..c64f62bf 100644
--- a/docs/userguide/commands.txt
+++ b/docs/userguide/commands.txt
@@ -521,7 +521,7 @@ result (which must be a ``unittest.TestSuite``) is added to the tests to be
run. If the named suite is a package, any submodules and subpackages are
recursively added to the overall test suite. (Note: if your project specifies
a ``test_loader``, the rules for processing the chosen ``test_suite`` may
-differ; see the `test_loader`_ documentation for more details.)
+differ; see the :ref:`test_loader <test_loader>` documentation for more details.)
Note that many test systems including ``doctest`` support wrapping their
non-``unittest`` tests in ``TestSuite`` objects. So, if you are using a test
@@ -563,4 +563,4 @@ The ``upload`` command was deprecated in version 40.0 and removed in version
For more information on the current best practices in uploading your packages
to PyPI, see the Python Packaging User Guide's "Packaging Python Projects"
tutorial specifically the section on `uploading the distribution archives
-<https://packaging.python.org/tutorials/packaging-projects/#uploading-the-distribution-archives>`_. \ No newline at end of file
+<https://packaging.python.org/tutorials/packaging-projects/#uploading-the-distribution-archives>`_.
diff --git a/setup.cfg b/setup.cfg
index 3933714b..72d4dce9 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -16,7 +16,7 @@ formats = zip
[metadata]
name = setuptools
-version = 46.3.1
+version = 46.4.0
description = Easily download, build, install, upgrade, and uninstall Python packages
author = Python Packaging Authority
author_email = distutils-sig@python.org
diff --git a/setuptools/config.py b/setuptools/config.py
index 0a2f51e2..45df2e3f 100644
--- a/setuptools/config.py
+++ b/setuptools/config.py
@@ -6,10 +6,11 @@ import sys
import warnings
import functools
+import importlib
from collections import defaultdict
from functools import partial
from functools import wraps
-from importlib import import_module
+import contextlib
from distutils.errors import DistutilsOptionError, DistutilsFileError
from setuptools.extern.packaging.version import LegacyVersion, parse
@@ -20,6 +21,44 @@ from setuptools.extern.six import string_types, PY3
__metaclass__ = type
+class StaticModule:
+ """
+ Attempt to load the module by the name
+ """
+ def __init__(self, name):
+ spec = importlib.util.find_spec(name)
+ with open(spec.origin) as strm:
+ src = strm.read()
+ module = ast.parse(src)
+ vars(self).update(locals())
+ del self.self
+
+ def __getattr__(self, attr):
+ try:
+ return next(
+ ast.literal_eval(statement.value)
+ for statement in self.module.body
+ if isinstance(statement, ast.Assign)
+ for target in statement.targets
+ if isinstance(target, ast.Name) and target.id == attr
+ )
+ except Exception:
+ raise AttributeError(
+ "{self.name} has no attribute {attr}".format(**locals()))
+
+
+@contextlib.contextmanager
+def patch_path(path):
+ """
+ Add path to front of sys.path for the duration of the context.
+ """
+ try:
+ sys.path.insert(0, path)
+ yield
+ finally:
+ sys.path.remove(path)
+
+
def read_configuration(
filepath, find_others=False, ignore_option_errors=False):
"""Read given configuration file and returns options from it as a dict.
@@ -346,50 +385,15 @@ class ConfigHandler:
# A custom parent directory was specified for all root modules
parent_path = os.path.join(os.getcwd(), package_dir[''])
- fpath = os.path.join(parent_path, *module_name.split('.'))
- if os.path.exists(fpath + '.py'):
- fpath += '.py'
- elif os.path.isdir(fpath):
- fpath = os.path.join(fpath, '__init__.py')
- else:
- raise DistutilsOptionError('Could not find module ' + module_name)
- with open(fpath, 'rb') as fp:
- src = fp.read()
- found = False
- top_level = ast.parse(src)
- for statement in top_level.body:
- if isinstance(statement, ast.Assign):
- for target in statement.targets:
- if isinstance(target, ast.Name) \
- and target.id == attr_name:
- try:
- value = ast.literal_eval(statement.value)
- except ValueError:
- found = False
- else:
- found = True
- elif isinstance(target, ast.Tuple) \
- and any(isinstance(t, ast.Name) and t.id == attr_name
- for t in target.elts):
- try:
- stmnt_value = ast.literal_eval(statement.value)
- except ValueError:
- found = False
- else:
- for t, v in zip(target.elts, stmnt_value):
- if isinstance(t, ast.Name) \
- and t.id == attr_name:
- value = v
- found = True
- if not found:
- # Fall back to extracting attribute via importing
- sys.path.insert(0, parent_path)
+ with patch_path(parent_path):
try:
- module = import_module(module_name)
- value = getattr(module, attr_name)
- finally:
- sys.path = sys.path[1:]
- return value
+ # attempt to load value statically
+ return getattr(StaticModule(module_name), attr_name)
+ except Exception:
+ # fallback to simple import
+ module = importlib.import_module(module_name)
+
+ return getattr(module, attr_name)
@classmethod
def _get_parser_compound(cls, *parse_methods):
diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py
index d8347c78..67992c04 100644
--- a/setuptools/tests/test_config.py
+++ b/setuptools/tests/test_config.py
@@ -2,6 +2,7 @@
from __future__ import unicode_literals
import contextlib
+
import pytest
from distutils.errors import DistutilsOptionError, DistutilsFileError
@@ -9,6 +10,7 @@ from mock import patch
from setuptools.dist import Distribution, _Distribution
from setuptools.config import ConfigHandler, read_configuration
from setuptools.extern.six.moves import configparser
+from setuptools.extern import six
from . import py2_only, py3_only
from .textwrap import DALS
@@ -53,6 +55,7 @@ def fake_env(
' return [3, 4, 5, "dev"]\n'
'\n'
)
+
return package_dir, config
@@ -103,7 +106,7 @@ class TestConfigurationReader:
'version = attr: none.VERSION\n'
'keywords = one, two\n'
)
- with pytest.raises(DistutilsOptionError):
+ with pytest.raises(ImportError):
read_configuration('%s' % config)
config_dict = read_configuration(
@@ -267,11 +270,23 @@ class TestMetadata:
def test_version(self, tmpdir):
- _, config = fake_env(
+ package_dir, config = fake_env(
tmpdir,
'[metadata]\n'
'version = attr: fake_package.VERSION\n'
)
+
+ sub_a = package_dir.mkdir('subpkg_a')
+ sub_a.join('__init__.py').write('')
+ sub_a.join('mod.py').write('VERSION = (2016, 11, 26)')
+
+ sub_b = package_dir.mkdir('subpkg_b')
+ sub_b.join('__init__.py').write('')
+ sub_b.join('mod.py').write(
+ 'import third_party_module\n'
+ 'VERSION = (2016, 11, 26)'
+ )
+
with get_dist(tmpdir) as dist:
assert dist.metadata.version == '1.2.3'
@@ -289,25 +304,20 @@ class TestMetadata:
with get_dist(tmpdir) as dist:
assert dist.metadata.version == '1'
- subpack = tmpdir.join('fake_package').mkdir('subpackage')
- subpack.join('__init__.py').write('')
- subpack.join('submodule.py').write('VERSION = (2016, 11, 26)')
-
config.write(
'[metadata]\n'
- 'version = attr: fake_package.subpackage.submodule.VERSION\n'
+ 'version = attr: fake_package.subpkg_a.mod.VERSION\n'
)
with get_dist(tmpdir) as dist:
assert dist.metadata.version == '2016.11.26'
- subpack.join('submodule.py').write(
- 'import third_party_module\n'
- 'VERSION = (2016, 11, 26)'
- )
+ if six.PY2:
+ # static version loading is unsupported on Python 2
+ return
config.write(
'[metadata]\n'
- 'version = attr: fake_package.subpackage.submodule.VERSION\n'
+ 'version = attr: fake_package.subpkg_b.mod.VERSION\n'
)
with get_dist(tmpdir) as dist:
assert dist.metadata.version == '2016.11.26'