diff --git a/doc/pyproject_toml.rst b/doc/pyproject_toml.rst index 8d38bf95..458eed1b 100644 --- a/doc/pyproject_toml.rst +++ b/doc/pyproject_toml.rst @@ -92,6 +92,8 @@ description-file (``.rst``, ``.md`` or ``.txt``). classifiers A list of `Trove classifiers `_. + Add ``Private :: Do Not Upload`` into the list to prevent a private package + from uploading on PyPI by accident. requires-python A version specifier for the versions of Python this requires, e.g. ``~=3.3`` or ``>=3.3,<4`` which are equivalents. diff --git a/flit/validate.py b/flit/validate.py index c1a26581..7184b1f5 100644 --- a/flit/validate.py +++ b/flit/validate.py @@ -13,6 +13,12 @@ log = logging.getLogger(__name__) +CUSTOM_CLASSIFIERS = frozenset({ + # https://github.com/pypa/warehouse/pull/5440 + 'Private :: Do Not Upload', +}) + + def get_cache_dir() -> Path: """Locate a platform-appropriate cache directory for flit to use @@ -96,6 +102,7 @@ def validate_classifiers(classifiers): classifiers = set(classifiers) try: valid_classifiers = _read_classifiers_cached() + valid_classifiers.update(CUSTOM_CLASSIFIERS) problems = _verify_classifiers(classifiers, valid_classifiers) except (FileNotFoundError, PermissionError) as e1: # We haven't yet got the classifiers cached or couldn't read it @@ -120,8 +127,8 @@ def validate_classifiers(classifiers): log.warning( "Couldn't get list of valid classifiers to check against") return problems - else: - return _verify_classifiers(classifiers, valid_classifiers) + valid_classifiers.update(CUSTOM_CLASSIFIERS) + return _verify_classifiers(classifiers, valid_classifiers) def validate_entrypoints(entrypoints): diff --git a/tests/test_validate.py b/tests/test_validate.py index e6dd3175..2c2da5fe 100644 --- a/tests/test_validate.py +++ b/tests/test_validate.py @@ -145,6 +145,26 @@ def mock_get_cache_dir(): assert classifiers == {"A", "B", "C"} +def test_validate_classifiers_private(monkeypatch): + """ + Test that `Private :: Do Not Upload` considered a valid classifier. + This is a special case because it is not listed in a trove classifier + but it is a way to make sure that a private package is not get uploaded + on PyPI by accident. + + Implementation on PyPI side: + https://github.com/pypa/warehouse/pull/5440 + Issue about officially documenting the trick: + https://github.com/pypa/packaging.python.org/issues/643 + """ + monkeypatch.setattr(fv, "_read_classifiers_cached", lambda: set()) + + actual = fv.validate_classifiers({'invalid'}) + assert actual == ["Unrecognised classifier: 'invalid'"] + + assert fv.validate_classifiers({'Private :: Do Not Upload'}) == [] + + @responses.activate @pytest.mark.parametrize("error", [PermissionError, OSError(errno.EROFS, "")]) def test_download_and_cache_classifiers_with_unacessible_dir(monkeypatch, error):