diff --git a/changelog.d/3613.misc.rst b/changelog.d/3613.misc.rst new file mode 100644 index 0000000000..7e64ed46aa --- /dev/null +++ b/changelog.d/3613.misc.rst @@ -0,0 +1 @@ +Fixed encoding errors in ``expand.StaticModule`` when system default encoding doesn't match expectations for source files. diff --git a/setuptools/config/expand.py b/setuptools/config/expand.py index 384504d879..38eb3db7d8 100644 --- a/setuptools/config/expand.py +++ b/setuptools/config/expand.py @@ -21,6 +21,7 @@ import importlib import io import os +import pathlib import sys import warnings from glob import iglob @@ -62,9 +63,7 @@ class StaticModule: """Proxy to a module object that avoids executing arbitrary code.""" def __init__(self, name: str, spec: ModuleSpec): - with open(spec.origin) as strm: # type: ignore - src = strm.read() - module = ast.parse(src) + module = ast.parse(pathlib.Path(spec.origin).read_bytes()) vars(self).update(locals()) del self.self diff --git a/setuptools/tests/config/test_expand.py b/setuptools/tests/config/test_expand.py index 523779a8ed..39f3b7c70f 100644 --- a/setuptools/tests/config/test_expand.py +++ b/setuptools/tests/config/test_expand.py @@ -60,6 +60,20 @@ def test_read_files(tmp_path, monkeypatch): class TestReadAttr: + @pytest.mark.parametrize( + "example", + [ + # No cookie means UTF-8: + b"__version__ = '\xc3\xa9'\nraise SystemExit(1)\n", + # If a cookie is present, honor it: + b"# -*- coding: utf-8 -*-\n__version__ = '\xc3\xa9'\nraise SystemExit(1)\n", + b"# -*- coding: latin1 -*-\n__version__ = '\xe9'\nraise SystemExit(1)\n", + ] + ) + def test_read_attr_encoding_cookie(self, example, tmp_path): + (tmp_path / "mod.py").write_bytes(example) + assert expand.read_attr('mod.__version__', root_dir=tmp_path) == 'é' + def test_read_attr(self, tmp_path, monkeypatch): files = { "pkg/__init__.py": "",