Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attempt on improving encoding in setuptools.tests #4261

Merged
merged 18 commits into from Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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