Skip to content

Commit

Permalink
Improve encoding in setuptools.tests (#4261)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
abravalheri committed Mar 7, 2024
2 parents ac3fd97 + 0eceb49 commit bac21fd
Show file tree
Hide file tree
Showing 22 changed files with 242 additions and 210 deletions.
1 change: 1 addition & 0 deletions newsfragments/4261.misc.rst
@@ -0,0 +1 @@
Avoid implicit ``encoding`` parameter in ``setuptools/tests``.
11 changes: 6 additions & 5 deletions setuptools/tests/config/test_apply_pyprojecttoml.py
Expand Up @@ -44,8 +44,9 @@ def test_apply_pyproject_equivalent_to_setupcfg(url, monkeypatch, tmp_path):
monkeypatch.setattr(expand, "read_attr", Mock(return_value="0.0.1"))
setupcfg_example = retrieve_file(url)
pyproject_example = Path(tmp_path, "pyproject.toml")
toml_config = Translator().translate(setupcfg_example.read_text(), "setup.cfg")
pyproject_example.write_text(toml_config)
setupcfg_text = setupcfg_example.read_text(encoding="utf-8")
toml_config = Translator().translate(setupcfg_text, "setup.cfg")
pyproject_example.write_text(toml_config, encoding="utf-8")

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

(tmp_path / readme).write_text("hello world")
(tmp_path / "LICENSE.txt").write_text("--- LICENSE stub ---")
(tmp_path / "spam.py").write_text(PEP621_EXAMPLE_SCRIPT)
(tmp_path / readme).write_text("hello world", encoding="utf-8")
(tmp_path / "LICENSE.txt").write_text("--- LICENSE stub ---", encoding="utf-8")
(tmp_path / "spam.py").write_text(PEP621_EXAMPLE_SCRIPT, encoding="utf-8")
return pyproject


Expand Down
2 changes: 1 addition & 1 deletion setuptools/tests/config/test_expand.py
Expand Up @@ -12,7 +12,7 @@ def write_files(files, root_dir):
for file, content in files.items():
path = root_dir / file
path.parent.mkdir(exist_ok=True, parents=True)
path.write_text(content)
path.write_text(content, encoding="utf-8")


def test_glob_relative(tmp_path, monkeypatch):
Expand Down
79 changes: 45 additions & 34 deletions setuptools/tests/config/test_pyprojecttoml.py
Expand Up @@ -2,6 +2,7 @@
from configparser import ConfigParser
from inspect import cleandoc

import jaraco.path
import pytest
import tomli_w
from path import Path
Expand Down Expand Up @@ -82,25 +83,32 @@


def create_example(path, pkg_root):
pyproject = path / "pyproject.toml"

files = [
f"{pkg_root}/pkg/__init__.py",
"_files/file.txt",
]
if pkg_root != ".": # flat-layout will raise error for multi-package dist
# Ensure namespaces are discovered
files.append(f"{pkg_root}/other/nested/__init__.py")
files = {
"pyproject.toml": EXAMPLE,
"README.md": "hello world",
"_files": {
"file.txt": "",
},
}
packages = {
"pkg": {
"__init__.py": "",
"mod.py": "class CustomSdist: pass",
"__version__.py": "VERSION = (3, 10)",
"__main__.py": "def exec(): print('hello')",
},
}

assert pkg_root # Meta-test: cannot be empty string.

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

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


def verify_example(config, path, pkg_root):
Expand Down Expand Up @@ -174,7 +182,7 @@ class TestEntryPoints:
def write_entry_points(self, tmp_path):
entry_points = ConfigParser()
entry_points.read_dict(ENTRY_POINTS)
with open(tmp_path / "entry-points.txt", "w") as f:
with open(tmp_path / "entry-points.txt", "w", encoding="utf-8") as f:
entry_points.write(f)

def pyproject(self, dynamic=None):
Expand Down Expand Up @@ -208,11 +216,13 @@ def test_dynamic(self, tmp_path):
# Let's create a project example that has dynamic classifiers
# coming from a txt file.
create_example(tmp_path, "src")
classifiers = """\
Framework :: Flask
Programming Language :: Haskell
"""
(tmp_path / "classifiers.txt").write_text(cleandoc(classifiers))
classifiers = cleandoc(
"""
Framework :: Flask
Programming Language :: Haskell
"""
)
(tmp_path / "classifiers.txt").write_text(classifiers, encoding="utf-8")

pyproject = tmp_path / "pyproject.toml"
config = read_configuration(pyproject, expand=False)
Expand Down Expand Up @@ -240,7 +250,7 @@ def test_dynamic_without_config(self, tmp_path):
"""

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

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

pyproject = tmp_path / "pyproject.toml"
pyproject.write_text(cleandoc(config))
pyproject.write_text(cleandoc(config), encoding="utf-8")
with pytest.warns(UserWarning, match="File .*classifiers.txt. cannot be found"):
expanded = read_configuration(pyproject)
assert "classifiers" not in expanded["project"]
Expand All @@ -291,7 +301,7 @@ def test_dynamic_without_file(self, tmp_path):
)
def test_ignore_unrelated_config(tmp_path, example):
pyproject = tmp_path / "pyproject.toml"
pyproject.write_text(cleandoc(example))
pyproject.write_text(cleandoc(example), encoding="utf-8")

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

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

# Make sure no error is raised
assert read_configuration(pyproject) == {}
Expand All @@ -335,7 +345,7 @@ def test_include_package_data_by_default(tmp_path, config):
default.
"""
pyproject = tmp_path / "pyproject.toml"
pyproject.write_text(config)
pyproject.write_text(config, encoding="utf-8")

config = read_configuration(pyproject)
assert config["tool"]["setuptools"]["include-package-data"] is True
Expand All @@ -347,10 +357,11 @@ def test_include_package_data_in_setuppy(tmp_path):
See https://github.com/pypa/setuptools/issues/3197#issuecomment-1079023889
"""
pyproject = tmp_path / "pyproject.toml"
pyproject.write_text("[project]\nname = 'myproj'\nversion='42'\n")
setuppy = tmp_path / "setup.py"
setuppy.write_text("__import__('setuptools').setup(include_package_data=False)")
files = {
"pyproject.toml": "[project]\nname = 'myproj'\nversion='42'\n",
"setup.py": "__import__('setuptools').setup(include_package_data=False)",
}
jaraco.path.build(files, prefix=tmp_path)

with Path(tmp_path):
dist = distutils.core.run_setup("setup.py", {}, stop_after="config")
Expand Down
111 changes: 58 additions & 53 deletions setuptools/tests/config/test_pyprojecttoml_dynamic_deps.py
@@ -1,57 +1,59 @@
from inspect import cleandoc

import pytest
from jaraco import path

from setuptools.config.pyprojecttoml import apply_configuration
from setuptools.dist import Distribution
from setuptools.tests.textwrap import DALS


def test_dynamic_dependencies(tmp_path):
(tmp_path / "requirements.txt").write_text("six\n # comment\n")
pyproject = tmp_path / "pyproject.toml"
pyproject.write_text(
DALS(
files = {
"requirements.txt": "six\n # comment\n",
"pyproject.toml": cleandoc(
"""
[project]
name = "myproj"
version = "1.0"
dynamic = ["dependencies"]
[project]
name = "myproj"
version = "1.0"
dynamic = ["dependencies"]
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
[tool.setuptools.dynamic.dependencies]
file = ["requirements.txt"]
"""
)
)
[tool.setuptools.dynamic.dependencies]
file = ["requirements.txt"]
"""
),
}
path.build(files, prefix=tmp_path)
dist = Distribution()
dist = apply_configuration(dist, pyproject)
dist = apply_configuration(dist, tmp_path / "pyproject.toml")
assert dist.install_requires == ["six"]


def test_dynamic_optional_dependencies(tmp_path):
(tmp_path / "requirements-docs.txt").write_text("sphinx\n # comment\n")
pyproject = tmp_path / "pyproject.toml"
pyproject.write_text(
DALS(
files = {
"requirements-docs.txt": "sphinx\n # comment\n",
"pyproject.toml": cleandoc(
"""
[project]
name = "myproj"
version = "1.0"
dynamic = ["optional-dependencies"]
[project]
name = "myproj"
version = "1.0"
dynamic = ["optional-dependencies"]
[tool.setuptools.dynamic.optional-dependencies.docs]
file = ["requirements-docs.txt"]
[tool.setuptools.dynamic.optional-dependencies.docs]
file = ["requirements-docs.txt"]
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
"""
)
)
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
"""
),
}
path.build(files, prefix=tmp_path)
dist = Distribution()
dist = apply_configuration(dist, pyproject)
dist = apply_configuration(dist, tmp_path / "pyproject.toml")
assert dist.extras_require == {"docs": ["sphinx"]}


Expand All @@ -61,29 +63,32 @@ def test_mixed_dynamic_optional_dependencies(tmp_path):
configurations in the case of fields containing sub-fields (groups),
things would work out.
"""
(tmp_path / "requirements-images.txt").write_text("pillow~=42.0\n # comment\n")
pyproject = tmp_path / "pyproject.toml"
pyproject.write_text(
DALS(
files = {
"requirements-images.txt": "pillow~=42.0\n # comment\n",
"pyproject.toml": cleandoc(
"""
[project]
name = "myproj"
version = "1.0"
dynamic = ["optional-dependencies"]
[project]
name = "myproj"
version = "1.0"
dynamic = ["optional-dependencies"]
[project.optional-dependencies]
docs = ["sphinx"]
[project.optional-dependencies]
docs = ["sphinx"]
[tool.setuptools.dynamic.optional-dependencies.images]
file = ["requirements-images.txt"]
[tool.setuptools.dynamic.optional-dependencies.images]
file = ["requirements-images.txt"]
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
"""
),
}

path.build(files, prefix=tmp_path)

[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
"""
)
)
# Test that the mix-and-match doesn't currently validate.
pyproject = tmp_path / "pyproject.toml"
with pytest.raises(ValueError, match="project.optional-dependencies"):
apply_configuration(Distribution(), pyproject)

Expand Down
3 changes: 2 additions & 1 deletion setuptools/tests/config/test_setupcfg.py
Expand Up @@ -904,7 +904,8 @@ def test_cmdclass(self, tmpdir):
module_path = Path(tmpdir, "src/custom_build.py") # auto discovery for src
module_path.parent.mkdir(parents=True, exist_ok=True)
module_path.write_text(
"from distutils.core import Command\n" "class CustomCmd(Command): pass\n"
"from distutils.core import Command\n" "class CustomCmd(Command): pass\n",
encoding="utf-8",
)

setup_cfg = """
Expand Down
1 change: 1 addition & 0 deletions setuptools/tests/integration/helpers.py
Expand Up @@ -17,6 +17,7 @@ def run(cmd, env=None):
cmd,
capture_output=True,
text=True,
encoding="utf-8",
env={**os.environ, **(env or {})},
# ^-- allow overwriting instead of discarding the current env
)
Expand Down

0 comments on commit bac21fd

Please sign in to comment.