diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ec15b95f7..18dc1b6c0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -176,6 +176,9 @@ supported: * `extra_description` (optional): Can be used to add a custom description to the package's long description. It should be a multi-line string in Markdown format. +* `stub_distribution` (optional): Distribution name to be uploaded to PyPI. + This defaults to `types-` and should only be set in special + cases. * `obsolete_since` (optional): This field is part of our process for [removing obsolete third-party libraries](#third-party-library-removal-policy). It contains the first version of the corresponding library that ships @@ -183,6 +186,9 @@ supported: * `no_longer_updated` (optional): This field is set to `true` before removing stubs for other reasons than the upstream library shipping with type information. +* `upload` (optional): This field is set to `false` to prevent automatic + uploads to PyPI. This should only used in special cases, e.g. when the stubs + break the upload. In addition, we specify configuration for stubtest in the `tool.stubtest` table. This has the following keys: diff --git a/tests/check_consistent.py b/tests/check_consistent.py index 260bcc9ac..4b2931524 100755 --- a/tests/check_consistent.py +++ b/tests/check_consistent.py @@ -17,11 +17,22 @@ from packaging.specifiers import SpecifierSet from packaging.version import Version from utils import VERSIONS_RE, get_all_testcase_directories, get_gitignore_spec, spec_matches_path, strip_comments -metadata_keys = {"version", "requires", "extra_description", "obsolete_since", "no_longer_updated", "tool"} +metadata_keys = { + "version", + "requires", + "extra_description", + "stub_distribution", + "obsolete_since", + "no_longer_updated", + "upload", + "tool", +} tool_keys = {"stubtest": {"skip", "apt_dependencies", "extras", "ignore_missing_stub", "platforms"}} extension_descriptions = {".pyi": "stub", ".py": ".py"} supported_stubtest_platforms = {"win32", "darwin", "linux"} +dist_name_re = re.compile(r"^[a-z0-9]([a-z0-9._-]*[a-z0-9])?$", re.IGNORECASE) + def assert_consistent_filetypes( directory: Path, *, kind: str, allowed: set[str], allow_nonidentifier_filenames: bool = False @@ -40,7 +51,7 @@ def assert_consistent_filetypes( if entry.is_file(): if not allow_nonidentifier_filenames: assert entry.stem.isidentifier(), f'Files must be valid modules, got: "{entry}"' - bad_filetype = f'Only {extension_descriptions[kind]!r} files allowed in the "{directory}" directory; got: {entry}' + bad_filetype = f'Only {extension_descriptions[kind]!r} files allowed in the "{directory}" directory; got: {entry}' assert entry.suffix == kind, bad_filetype else: assert entry.name.isidentifier(), f"Directories must be valid packages, got: {entry}" @@ -150,6 +161,11 @@ def check_metadata() -> None: # Check that the requirement parses Requirement(dep) + if "stub_distribution" in data: + assert dist_name_re.fullmatch(data["stub_distribution"]), f"Invalid 'stub_distribution' value for {distribution!r}" + + assert isinstance(data.get("upload", True), bool), f"Invalid 'upload' value for {distribution!r}" + assert set(data.get("tool", [])).issubset(tool_keys.keys()), f"Unrecognised tool for {distribution}" for tool, tk in tool_keys.items(): for key in data.get("tool", {}).get(tool, {}):