Skip to content

Commit bac21fd

Browse files
authored
Improve encoding in setuptools.tests (#4261)
The following approaches were used: - Simply add `encoding="utf-8"` - Use `open(..., "wb").close()` instead of `open(..., "w").close()` when appropriate - Use `jaraco.path.build` instead of `Path.write_text` when appropriate.
2 parents ac3fd97 + 0eceb49 commit bac21fd

22 files changed

+242
-210
lines changed

newsfragments/4261.misc.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Avoid implicit ``encoding`` parameter in ``setuptools/tests``.

setuptools/tests/config/test_apply_pyprojecttoml.py

+6-5
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,9 @@ def test_apply_pyproject_equivalent_to_setupcfg(url, monkeypatch, tmp_path):
4444
monkeypatch.setattr(expand, "read_attr", Mock(return_value="0.0.1"))
4545
setupcfg_example = retrieve_file(url)
4646
pyproject_example = Path(tmp_path, "pyproject.toml")
47-
toml_config = Translator().translate(setupcfg_example.read_text(), "setup.cfg")
48-
pyproject_example.write_text(toml_config)
47+
setupcfg_text = setupcfg_example.read_text(encoding="utf-8")
48+
toml_config = Translator().translate(setupcfg_text, "setup.cfg")
49+
pyproject_example.write_text(toml_config, encoding="utf-8")
4950

5051
dist_toml = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject_example)
5152
dist_cfg = setupcfg.apply_configuration(makedist(tmp_path), setupcfg_example)
@@ -177,9 +178,9 @@ def _pep621_example_project(
177178
text = text.replace(orig, subst)
178179
pyproject.write_text(text, encoding="utf-8")
179180

180-
(tmp_path / readme).write_text("hello world")
181-
(tmp_path / "LICENSE.txt").write_text("--- LICENSE stub ---")
182-
(tmp_path / "spam.py").write_text(PEP621_EXAMPLE_SCRIPT)
181+
(tmp_path / readme).write_text("hello world", encoding="utf-8")
182+
(tmp_path / "LICENSE.txt").write_text("--- LICENSE stub ---", encoding="utf-8")
183+
(tmp_path / "spam.py").write_text(PEP621_EXAMPLE_SCRIPT, encoding="utf-8")
183184
return pyproject
184185

185186

setuptools/tests/config/test_expand.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def write_files(files, root_dir):
1212
for file, content in files.items():
1313
path = root_dir / file
1414
path.parent.mkdir(exist_ok=True, parents=True)
15-
path.write_text(content)
15+
path.write_text(content, encoding="utf-8")
1616

1717

1818
def test_glob_relative(tmp_path, monkeypatch):

setuptools/tests/config/test_pyprojecttoml.py

+45-34
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from configparser import ConfigParser
33
from inspect import cleandoc
44

5+
import jaraco.path
56
import pytest
67
import tomli_w
78
from path import Path
@@ -82,25 +83,32 @@
8283

8384

8485
def create_example(path, pkg_root):
85-
pyproject = path / "pyproject.toml"
86-
87-
files = [
88-
f"{pkg_root}/pkg/__init__.py",
89-
"_files/file.txt",
90-
]
91-
if pkg_root != ".": # flat-layout will raise error for multi-package dist
92-
# Ensure namespaces are discovered
93-
files.append(f"{pkg_root}/other/nested/__init__.py")
86+
files = {
87+
"pyproject.toml": EXAMPLE,
88+
"README.md": "hello world",
89+
"_files": {
90+
"file.txt": "",
91+
},
92+
}
93+
packages = {
94+
"pkg": {
95+
"__init__.py": "",
96+
"mod.py": "class CustomSdist: pass",
97+
"__version__.py": "VERSION = (3, 10)",
98+
"__main__.py": "def exec(): print('hello')",
99+
},
100+
}
101+
102+
assert pkg_root # Meta-test: cannot be empty string.
94103

95-
for file in files:
96-
(path / file).parent.mkdir(exist_ok=True, parents=True)
97-
(path / file).touch()
104+
if pkg_root == ".":
105+
files = {**files, **packages}
106+
# skip other files: flat-layout will raise error for multi-package dist
107+
else:
108+
# Use this opportunity to ensure namespaces are discovered
109+
files[pkg_root] = {**packages, "other": {"nested": {"__init__.py": ""}}}
98110

99-
pyproject.write_text(EXAMPLE)
100-
(path / "README.md").write_text("hello world")
101-
(path / f"{pkg_root}/pkg/mod.py").write_text("class CustomSdist: pass")
102-
(path / f"{pkg_root}/pkg/__version__.py").write_text("VERSION = (3, 10)")
103-
(path / f"{pkg_root}/pkg/__main__.py").write_text("def exec(): print('hello')")
111+
jaraco.path.build(files, prefix=path)
104112

105113

106114
def verify_example(config, path, pkg_root):
@@ -174,7 +182,7 @@ class TestEntryPoints:
174182
def write_entry_points(self, tmp_path):
175183
entry_points = ConfigParser()
176184
entry_points.read_dict(ENTRY_POINTS)
177-
with open(tmp_path / "entry-points.txt", "w") as f:
185+
with open(tmp_path / "entry-points.txt", "w", encoding="utf-8") as f:
178186
entry_points.write(f)
179187

180188
def pyproject(self, dynamic=None):
@@ -208,11 +216,13 @@ def test_dynamic(self, tmp_path):
208216
# Let's create a project example that has dynamic classifiers
209217
# coming from a txt file.
210218
create_example(tmp_path, "src")
211-
classifiers = """\
212-
Framework :: Flask
213-
Programming Language :: Haskell
214-
"""
215-
(tmp_path / "classifiers.txt").write_text(cleandoc(classifiers))
219+
classifiers = cleandoc(
220+
"""
221+
Framework :: Flask
222+
Programming Language :: Haskell
223+
"""
224+
)
225+
(tmp_path / "classifiers.txt").write_text(classifiers, encoding="utf-8")
216226

217227
pyproject = tmp_path / "pyproject.toml"
218228
config = read_configuration(pyproject, expand=False)
@@ -240,7 +250,7 @@ def test_dynamic_without_config(self, tmp_path):
240250
"""
241251

242252
pyproject = tmp_path / "pyproject.toml"
243-
pyproject.write_text(cleandoc(config))
253+
pyproject.write_text(cleandoc(config), encoding="utf-8")
244254
with pytest.raises(OptionError, match="No configuration .* .classifiers."):
245255
read_configuration(pyproject)
246256

@@ -252,7 +262,7 @@ def test_dynamic_readme_from_setup_script_args(self, tmp_path):
252262
dynamic = ["readme"]
253263
"""
254264
pyproject = tmp_path / "pyproject.toml"
255-
pyproject.write_text(cleandoc(config))
265+
pyproject.write_text(cleandoc(config), encoding="utf-8")
256266
dist = Distribution(attrs={"long_description": "42"})
257267
# No error should occur because of missing `readme`
258268
dist = apply_configuration(dist, pyproject)
@@ -270,7 +280,7 @@ def test_dynamic_without_file(self, tmp_path):
270280
"""
271281

272282
pyproject = tmp_path / "pyproject.toml"
273-
pyproject.write_text(cleandoc(config))
283+
pyproject.write_text(cleandoc(config), encoding="utf-8")
274284
with pytest.warns(UserWarning, match="File .*classifiers.txt. cannot be found"):
275285
expanded = read_configuration(pyproject)
276286
assert "classifiers" not in expanded["project"]
@@ -291,7 +301,7 @@ def test_dynamic_without_file(self, tmp_path):
291301
)
292302
def test_ignore_unrelated_config(tmp_path, example):
293303
pyproject = tmp_path / "pyproject.toml"
294-
pyproject.write_text(cleandoc(example))
304+
pyproject.write_text(cleandoc(example), encoding="utf-8")
295305

296306
# Make sure no error is raised due to 3rd party configs in pyproject.toml
297307
assert read_configuration(pyproject) is not None
@@ -313,7 +323,7 @@ def test_ignore_unrelated_config(tmp_path, example):
313323
)
314324
def test_invalid_example(tmp_path, example, error_msg):
315325
pyproject = tmp_path / "pyproject.toml"
316-
pyproject.write_text(cleandoc(example))
326+
pyproject.write_text(cleandoc(example), encoding="utf-8")
317327

318328
pattern = re.compile(f"invalid pyproject.toml.*{error_msg}.*", re.M | re.S)
319329
with pytest.raises(ValueError, match=pattern):
@@ -323,7 +333,7 @@ def test_invalid_example(tmp_path, example, error_msg):
323333
@pytest.mark.parametrize("config", ("", "[tool.something]\nvalue = 42"))
324334
def test_empty(tmp_path, config):
325335
pyproject = tmp_path / "pyproject.toml"
326-
pyproject.write_text(config)
336+
pyproject.write_text(config, encoding="utf-8")
327337

328338
# Make sure no error is raised
329339
assert read_configuration(pyproject) == {}
@@ -335,7 +345,7 @@ def test_include_package_data_by_default(tmp_path, config):
335345
default.
336346
"""
337347
pyproject = tmp_path / "pyproject.toml"
338-
pyproject.write_text(config)
348+
pyproject.write_text(config, encoding="utf-8")
339349

340350
config = read_configuration(pyproject)
341351
assert config["tool"]["setuptools"]["include-package-data"] is True
@@ -347,10 +357,11 @@ def test_include_package_data_in_setuppy(tmp_path):
347357
348358
See https://github.com/pypa/setuptools/issues/3197#issuecomment-1079023889
349359
"""
350-
pyproject = tmp_path / "pyproject.toml"
351-
pyproject.write_text("[project]\nname = 'myproj'\nversion='42'\n")
352-
setuppy = tmp_path / "setup.py"
353-
setuppy.write_text("__import__('setuptools').setup(include_package_data=False)")
360+
files = {
361+
"pyproject.toml": "[project]\nname = 'myproj'\nversion='42'\n",
362+
"setup.py": "__import__('setuptools').setup(include_package_data=False)",
363+
}
364+
jaraco.path.build(files, prefix=tmp_path)
354365

355366
with Path(tmp_path):
356367
dist = distutils.core.run_setup("setup.py", {}, stop_after="config")

setuptools/tests/config/test_pyprojecttoml_dynamic_deps.py

+58-53
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,59 @@
1+
from inspect import cleandoc
2+
13
import pytest
4+
from jaraco import path
25

36
from setuptools.config.pyprojecttoml import apply_configuration
47
from setuptools.dist import Distribution
5-
from setuptools.tests.textwrap import DALS
68

79

810
def test_dynamic_dependencies(tmp_path):
9-
(tmp_path / "requirements.txt").write_text("six\n # comment\n")
10-
pyproject = tmp_path / "pyproject.toml"
11-
pyproject.write_text(
12-
DALS(
11+
files = {
12+
"requirements.txt": "six\n # comment\n",
13+
"pyproject.toml": cleandoc(
1314
"""
14-
[project]
15-
name = "myproj"
16-
version = "1.0"
17-
dynamic = ["dependencies"]
15+
[project]
16+
name = "myproj"
17+
version = "1.0"
18+
dynamic = ["dependencies"]
1819
19-
[build-system]
20-
requires = ["setuptools", "wheel"]
21-
build-backend = "setuptools.build_meta"
20+
[build-system]
21+
requires = ["setuptools", "wheel"]
22+
build-backend = "setuptools.build_meta"
2223
23-
[tool.setuptools.dynamic.dependencies]
24-
file = ["requirements.txt"]
25-
"""
26-
)
27-
)
24+
[tool.setuptools.dynamic.dependencies]
25+
file = ["requirements.txt"]
26+
"""
27+
),
28+
}
29+
path.build(files, prefix=tmp_path)
2830
dist = Distribution()
29-
dist = apply_configuration(dist, pyproject)
31+
dist = apply_configuration(dist, tmp_path / "pyproject.toml")
3032
assert dist.install_requires == ["six"]
3133

3234

3335
def test_dynamic_optional_dependencies(tmp_path):
34-
(tmp_path / "requirements-docs.txt").write_text("sphinx\n # comment\n")
35-
pyproject = tmp_path / "pyproject.toml"
36-
pyproject.write_text(
37-
DALS(
36+
files = {
37+
"requirements-docs.txt": "sphinx\n # comment\n",
38+
"pyproject.toml": cleandoc(
3839
"""
39-
[project]
40-
name = "myproj"
41-
version = "1.0"
42-
dynamic = ["optional-dependencies"]
40+
[project]
41+
name = "myproj"
42+
version = "1.0"
43+
dynamic = ["optional-dependencies"]
4344
44-
[tool.setuptools.dynamic.optional-dependencies.docs]
45-
file = ["requirements-docs.txt"]
45+
[tool.setuptools.dynamic.optional-dependencies.docs]
46+
file = ["requirements-docs.txt"]
4647
47-
[build-system]
48-
requires = ["setuptools", "wheel"]
49-
build-backend = "setuptools.build_meta"
50-
"""
51-
)
52-
)
48+
[build-system]
49+
requires = ["setuptools", "wheel"]
50+
build-backend = "setuptools.build_meta"
51+
"""
52+
),
53+
}
54+
path.build(files, prefix=tmp_path)
5355
dist = Distribution()
54-
dist = apply_configuration(dist, pyproject)
56+
dist = apply_configuration(dist, tmp_path / "pyproject.toml")
5557
assert dist.extras_require == {"docs": ["sphinx"]}
5658

5759

@@ -61,29 +63,32 @@ def test_mixed_dynamic_optional_dependencies(tmp_path):
6163
configurations in the case of fields containing sub-fields (groups),
6264
things would work out.
6365
"""
64-
(tmp_path / "requirements-images.txt").write_text("pillow~=42.0\n # comment\n")
65-
pyproject = tmp_path / "pyproject.toml"
66-
pyproject.write_text(
67-
DALS(
66+
files = {
67+
"requirements-images.txt": "pillow~=42.0\n # comment\n",
68+
"pyproject.toml": cleandoc(
6869
"""
69-
[project]
70-
name = "myproj"
71-
version = "1.0"
72-
dynamic = ["optional-dependencies"]
70+
[project]
71+
name = "myproj"
72+
version = "1.0"
73+
dynamic = ["optional-dependencies"]
7374
74-
[project.optional-dependencies]
75-
docs = ["sphinx"]
75+
[project.optional-dependencies]
76+
docs = ["sphinx"]
7677
77-
[tool.setuptools.dynamic.optional-dependencies.images]
78-
file = ["requirements-images.txt"]
78+
[tool.setuptools.dynamic.optional-dependencies.images]
79+
file = ["requirements-images.txt"]
80+
81+
[build-system]
82+
requires = ["setuptools", "wheel"]
83+
build-backend = "setuptools.build_meta"
84+
"""
85+
),
86+
}
87+
88+
path.build(files, prefix=tmp_path)
7989

80-
[build-system]
81-
requires = ["setuptools", "wheel"]
82-
build-backend = "setuptools.build_meta"
83-
"""
84-
)
85-
)
8690
# Test that the mix-and-match doesn't currently validate.
91+
pyproject = tmp_path / "pyproject.toml"
8792
with pytest.raises(ValueError, match="project.optional-dependencies"):
8893
apply_configuration(Distribution(), pyproject)
8994

setuptools/tests/config/test_setupcfg.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -904,7 +904,8 @@ def test_cmdclass(self, tmpdir):
904904
module_path = Path(tmpdir, "src/custom_build.py") # auto discovery for src
905905
module_path.parent.mkdir(parents=True, exist_ok=True)
906906
module_path.write_text(
907-
"from distutils.core import Command\n" "class CustomCmd(Command): pass\n"
907+
"from distutils.core import Command\n" "class CustomCmd(Command): pass\n",
908+
encoding="utf-8",
908909
)
909910

910911
setup_cfg = """

setuptools/tests/integration/helpers.py

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ def run(cmd, env=None):
1717
cmd,
1818
capture_output=True,
1919
text=True,
20+
encoding="utf-8",
2021
env={**os.environ, **(env or {})},
2122
# ^-- allow overwriting instead of discarding the current env
2223
)

0 commit comments

Comments
 (0)