Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions docs/sources/reference/generated-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,21 @@ Includes pyupgrade, isort, black, zpretty, flake8, codespell, check-manifest, py

**Template:** {file}`pyproject.toml.j2`

**Purpose:** Python tooling configuration for isort, black, codespell, check-manifest, and z3c.dependencychecker.
Also includes towncrier configuration if a {file}`news/` folder exists.
**Purpose:** Python distribution metadata and tooling configuration.

Contrary to all other generated files, {file}`pyproject.toml` keeps whatever is within some special markers.

These markers are meant to be around the `[project]` table, it should look like:

```toml
# START-MARKER-MANUAL-CONFIG
[project]
name = "plone.meta"
version = "1.2.4"
...
# END-MARKER-MANUAL-CONFIG
```

:::{note}
plone.meta overwrites {file}`pyproject.toml` completely, like all other generated files. All customization must go through {file}`.meta.toml`.
:::

## tox.ini

Expand Down
1 change: 1 addition & 0 deletions news/315.feature.2
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Ensure the `[project]` table is not removed from `pyproject.toml` @gforcada
17 changes: 17 additions & 0 deletions src/plone/meta/config_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from functools import cached_property
from importlib.metadata import version
from packaging.version import Version
from pathlib import Path

import argparse
import collections
Expand Down Expand Up @@ -425,13 +426,29 @@ def pyproject_toml(self):
"If you want to use Towncrier, you have to create a 'news/' folder manually.",
)

options["project_metadata"] = self._get_manual_metadata()

filename = self.copy_with_meta(
"pyproject.toml.j2",
**options,
)
files.append(filename)
return files

def _get_manual_metadata(self):
metadata = ""
suffix = "MARKER-MANUAL-CONFIG"
pyproject_path = Path(self.path) / "pyproject.toml"
if not pyproject_path.exists():
return ""
actual_pyproject = pyproject_path.read_text()
start_marker = actual_pyproject.find(f"# START-{suffix}")
end_marker = actual_pyproject.find(f"# END-{suffix}")
if start_marker > -1 and end_marker > -1:
end_marker = end_marker + len(f"# END-{suffix}")
metadata = actual_pyproject[start_marker:end_marker]
return metadata

def tox(self):
options = self._get_options_for(
"tox",
Expand Down
2 changes: 2 additions & 0 deletions src/plone/meta/default/pyproject.toml.j2
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
[build-system]
requires = ["setuptools>=68.2,<%(setuptools_upper_bound)s", "wheel"]

%(project_metadata)s

{% if news_folder_exists %}
[tool.towncrier]
directory = "news/"
Expand Down
14 changes: 13 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,23 @@ def mock_args(mock_git_repo):


@pytest.fixture
def package_config(meta_toml_factory, mock_args):
def pyproject_toml(mock_git_repo):
"""Create an empty pyproject.toml"""

def _create():
toml_path = mock_git_repo / "pyproject.toml"
toml_path.touch()

return _create


@pytest.fixture
def package_config(pyproject_toml, meta_toml_factory, mock_args):
"""Create a PackageConfiguration with mocked subprocess calls."""
from plone.meta.config_package import PackageConfiguration

meta_toml_factory() # creates default .meta.toml
pyproject_toml() # creates an empty pyproject.toml
with (
patch(
"plone.meta.config_package.git_server_url",
Expand Down
19 changes: 18 additions & 1 deletion tests/test_package_config_ci.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,11 @@ def test_flake8(self, package_config):

class TestPyproject:
def test_minimal_files(self, package_config):
pyproject_file_path = package_config.path / "pyproject.toml"
result = package_config.pyproject_toml()
assert len(result) == 1
assert (package_config.path / "pyproject.toml").exists()
text = pyproject_file_path.read_text()
assert len(text.splitlines()) > 50

def test_if_news_folder_exists(self, package_config):
(package_config.path / "news").mkdir(parents=True, exist_ok=True)
Expand All @@ -96,6 +98,21 @@ def test_if_changes_md_exists(self, package_config):
assert len(result) == 2
assert (package_config.path / "news" / ".changelog_template.jinja").exists()

def test_metadata_is_kept(self, package_config):
pyproject_file_path = package_config.path / "pyproject.toml"
text = [
"# START-MARKER-MANUAL-CONFIG",
"[project]",
'name="random-project"',
"# END-MARKER-MANUAL-CONFIG",
]
pyproject_file_path.write_text("\n".join(text))
package_config.pyproject_toml()
final_toml_text = pyproject_file_path.read_text()
assert len(final_toml_text.splitlines()) > len(text)
for line in text:
assert line in final_toml_text


class TestSetuptoolsUpperBound:
@pytest.mark.parametrize(["is_native", "expected"], [[True, "82"], [False, "83"]])
Expand Down