diff --git a/mypy.ini b/mypy.ini index 45671826b1..4eebe5f77c 100644 --- a/mypy.ini +++ b/mypy.ini @@ -15,10 +15,11 @@ exclude = (?x)( | ^setuptools/_distutils/ # Vendored | ^setuptools/config/_validate_pyproject/ # Auto-generated ) + # Ignoring attr-defined because setuptools wraps a lot of distutils classes, adding new attributes, # w/o updating all the attributes and return types from the base classes for type-checkers to understand # Especially with setuptools.dist.command vs distutils.dist.command vs setuptools._distutils.dist.command -# *.extern modules that actually live in *._vendor will also cause attr-defined issues on import +[mypy-setuptools.*] disable_error_code = attr-defined # - Avoid raising issues when importing from "extern" modules, as those are added to path dynamically. diff --git a/newsfragments/4348.bugfix.rst b/newsfragments/4348.bugfix.rst new file mode 100644 index 0000000000..a8bb79a123 --- /dev/null +++ b/newsfragments/4348.bugfix.rst @@ -0,0 +1 @@ +Fix an error with `UnicodeDecodeError` handling in ``pkg_resources`` when trying to read files in UTF-8 with a fallback -- by :user:`Avasam` diff --git a/newsfragments/4348.misc.rst b/newsfragments/4348.misc.rst new file mode 100644 index 0000000000..989226c4b3 --- /dev/null +++ b/newsfragments/4348.misc.rst @@ -0,0 +1 @@ +Update dynamic module imports in ``pkg_resources`` to private alias static imports. Enabled ``attr-defined`` checks in mypy for ``pkg_resources`` -- by :user:`Avasam` diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 1cd657fb6d..b595ec5965 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -70,15 +70,11 @@ drop_comment, join_continuation, ) - -from pkg_resources.extern import platformdirs -from pkg_resources.extern import packaging - -__import__('pkg_resources.extern.packaging.version') -__import__('pkg_resources.extern.packaging.specifiers') -__import__('pkg_resources.extern.packaging.requirements') -__import__('pkg_resources.extern.packaging.markers') -__import__('pkg_resources.extern.packaging.utils') +from pkg_resources.extern.packaging import markers as _packaging_markers +from pkg_resources.extern.packaging import requirements as _packaging_requirements +from pkg_resources.extern.packaging import utils as _packaging_utils +from pkg_resources.extern.packaging import version as _packaging_version +from pkg_resources.extern.platformdirs import user_cache_dir # declare some globals that will be defined later to # satisfy the linters. @@ -116,7 +112,7 @@ class PEP440Warning(RuntimeWarning): """ -parse_version = packaging.version.Version +parse_version = _packaging_version.Version _state_vars: Dict[str, str] = {} @@ -730,7 +726,7 @@ def add(self, dist, entry=None, insert=True, replace=False): return self.by_key[dist.key] = dist - normalized_name = packaging.utils.canonicalize_name(dist.key) + normalized_name = _packaging_utils.canonicalize_name(dist.key) self.normalized_to_canonical_keys[normalized_name] = dist.key if dist.key not in keys: keys.append(dist.key) @@ -1344,9 +1340,7 @@ def get_default_cache(): or a platform-relevant user cache dir for an app named "Python-Eggs". """ - return os.environ.get('PYTHON_EGG_CACHE') or platformdirs.user_cache_dir( - appname='Python-Eggs' - ) + return os.environ.get('PYTHON_EGG_CACHE') or user_cache_dir(appname='Python-Eggs') def safe_name(name): @@ -1363,8 +1357,8 @@ def safe_version(version): """ try: # normalize the version - return str(packaging.version.Version(version)) - except packaging.version.InvalidVersion: + return str(_packaging_version.Version(version)) + except _packaging_version.InvalidVersion: version = version.replace(' ', '.') return re.sub('[^A-Za-z0-9.]+', '-', version) @@ -1441,9 +1435,9 @@ def evaluate_marker(text, extra=None): This implementation uses the 'pyparsing' module. """ try: - marker = packaging.markers.Marker(text) + marker = _packaging_markers.Marker(text) return marker.evaluate() - except packaging.markers.InvalidMarker as e: + except _packaging_markers.InvalidMarker as e: raise SyntaxError(e) from e @@ -2695,12 +2689,12 @@ def parsed_version(self): if not hasattr(self, "_parsed_version"): try: self._parsed_version = parse_version(self.version) - except packaging.version.InvalidVersion as ex: + except _packaging_version.InvalidVersion as ex: info = f"(package: {self.project_name})" if hasattr(ex, "add_note"): ex.add_note(info) # PEP 678 raise - raise packaging.version.InvalidVersion(f"{str(ex)} {info}") from None + raise _packaging_version.InvalidVersion(f"{str(ex)} {info}") from None return self._parsed_version @@ -2708,7 +2702,7 @@ def parsed_version(self): def _forgiving_parsed_version(self): try: return self.parsed_version - except packaging.version.InvalidVersion as ex: + except _packaging_version.InvalidVersion as ex: self._parsed_version = parse_version(_forgiving_version(self.version)) notes = "\n".join(getattr(ex, "__notes__", [])) # PEP 678 @@ -2881,7 +2875,7 @@ def from_filename(cls, filename, metadata=None, **kw): def as_requirement(self): """Return a ``Requirement`` that matches this distribution exactly""" - if isinstance(self.parsed_version, packaging.version.Version): + if isinstance(self.parsed_version, _packaging_version.Version): spec = "%s==%s" % (self.project_name, self.parsed_version) else: spec = "%s===%s" % (self.project_name, self.parsed_version) @@ -3127,11 +3121,11 @@ def parse_requirements(strs): return map(Requirement, join_continuation(map(drop_comment, yield_lines(strs)))) -class RequirementParseError(packaging.requirements.InvalidRequirement): +class RequirementParseError(_packaging_requirements.InvalidRequirement): "Compatibility wrapper for InvalidRequirement" -class Requirement(packaging.requirements.Requirement): +class Requirement(_packaging_requirements.Requirement): def __init__(self, requirement_string): """DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!""" super().__init__(requirement_string) @@ -3353,6 +3347,6 @@ def _read_utf8_with_fallback(file: str, fallback_encoding=LOCALE_ENCODING) -> st """ # TODO: Add a deadline? # See comment in setuptools.unicode_utils._Utf8EncodingNeeded - warnings.warns(msg, PkgResourcesDeprecationWarning, stacklevel=2) + warnings.warn(msg, PkgResourcesDeprecationWarning, stacklevel=2) with open(file, "r", encoding=fallback_encoding) as f: return f.read() diff --git a/pkg_resources/tests/test_resources.py b/pkg_resources/tests/test_resources.py index b0a319e60f..83199af7b8 100644 --- a/pkg_resources/tests/test_resources.py +++ b/pkg_resources/tests/test_resources.py @@ -5,7 +5,7 @@ import itertools import pytest -from pkg_resources.extern import packaging +from pkg_resources.extern.packaging.specifiers import SpecifierSet import pkg_resources from pkg_resources import ( @@ -567,7 +567,7 @@ def testOptionsAndHashing(self): assert hash(r1) == hash(( "twisted", None, - packaging.specifiers.SpecifierSet(">=1.2"), + SpecifierSet(">=1.2"), frozenset(["foo", "bar"]), None, )) @@ -576,7 +576,7 @@ def testOptionsAndHashing(self): ) == hash(( "twisted", "https://localhost/twisted.zip", - packaging.specifiers.SpecifierSet(), + SpecifierSet(), frozenset(), None, ))