diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c858948..c58a1213 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -160,9 +160,6 @@ jobs: - name: Check Newsfragment nox: check_newsfragment run-if: ${{ github.head_ref != 'pre-commit-ci-update-config' }} - - name: Check package manifest - nox: check_manifest - run-if: true - name: Check mypy nox: typecheck run-if: true diff --git a/.gitignore b/.gitignore index c44127a6..fa4a8cd0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,25 +1,28 @@ -*.egg-info/ +*.lock *.o *.py[co] +*.pyproj *.so -_trial_temp*/ -build/ -dropin.cache -doc/ -docs/_build/ -dist/ -venv/ -htmlcov/ -.coverage *~ -*.lock -apidocs/ -.vs/ -*.pyproj .DS_Store -.eggs -.nox/ +.coverage .coverage.* -.vscode +.direnv +.envrc .idea +.mypy_cache +.nox/ +.pytest_cache .python-version +.vs/ +.vscode +Justfile +_trial_temp*/ +apidocs/ +dist/ +doc/ +docs/_build/ +dropin.cache +htmlcov/ +tmp/ +venv/ diff --git a/.readthedocs.yml b/.readthedocs.yaml similarity index 100% rename from .readthedocs.yml rename to .readthedocs.yaml diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index e69f970b..661f22e4 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -128,7 +128,7 @@ The following list contains some ways how to run the test suite: * To run some quality checks before you create the pull request, we recommend using this call:: - $ nox -e pre_commit check_manifest check_newsfragment + $ nox -e pre_commit check_newsfragment * Or enable `pre-commit` as a git hook:: diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 9494555b..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,20 +0,0 @@ -include *.rst -include .coveragerc -include LICENSE -include CODE_OF_CONDUCT.md -include pyproject.toml -include noxfile.py -include *.yaml -include .git-blame-ignore-revs -include .flake8 -recursive-include src *.rst - -exclude bin -exclude admin -exclude src/towncrier/newsfragments -exclude .readthedocs.yml - -recursive-exclude bin * -recursive-exclude admin * -recursive-exclude src/towncrier/newsfragments * -recursive-exclude docs * diff --git a/noxfile.py b/noxfile.py index 38b65b98..607f89f9 100644 --- a/noxfile.py +++ b/noxfile.py @@ -55,12 +55,6 @@ def check_newsfragment(session: nox.Session) -> None: session.run("python", "-m", "towncrier.check", "--compare-with", "origin/trunk") -@nox.session -def check_manifest(session: nox.Session) -> None: - session.install("check-manifest") - session.run("check-manifest") - - @nox.session def typecheck(session: nox.Session) -> None: session.install(".", "mypy", "types-setuptools") @@ -87,11 +81,10 @@ def docs(session: nox.Session) -> None: @nox.session def build(session: nox.Session) -> None: - session.install("build", "check-manifest>=0.44", "twine") + session.install("build", "twine") - session.run("check-manifest", "--verbose") # If no argument is passed, build builds an sdist and then a wheel from # that sdist. session.run("python", "-m", "build") - session.run("twine", "check", "dist/*") + session.run("twine", "check", "--strict", "dist/*") diff --git a/pyproject.toml b/pyproject.toml index c2145ba6..32a2c3c2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,80 @@ +[build-system] +requires = [ + "hatchling ~= 1.12.2", + "wheel ~= 0.38.4", + "incremental == 22.10.0", +] +build-backend = "hatchling.build" + + +[project] +dynamic = ["version"] +name = "towncrier" +description = "Building newsfiles for your project." +readme = "README.rst" +license = "MIT" +classifiers = [ + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: POSIX :: Linux", + "Operating System :: MacOS :: MacOS X", + "Operating System :: Microsoft :: Windows", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", +] +requires-python = ">=3.7" +dependencies = [ + "click", + "click-default-group", + "incremental", + "jinja2", + "setuptools", + "tomli; python_version<'3.11'", +] + +[project.optional-dependencies] +dev = [ + "packaging", + "sphinx >= 5", + "furo", + "twisted", +] + +[project.scripts] +towncrier = "towncrier._shell:cli" + +[project.urls] +Documentation = "https://towncrier.readthedocs.io/" +Chat = "https://web.libera.chat/?channels=%23twisted" +"Mailing list" = "https://mail.python.org/mailman3/lists/twisted.python.org/" +Issues = "https://github.com/twisted/towncrier/issues" +Repository = "https://github.com/twisted/towncrier" +Tests = "https://github.com/twisted/towncrier/actions?query=branch%3Atrunk" +Coverage = "https://codecov.io/gh/twisted/towncrier" +Distribution = "https://pypi.org/project/towncrier" + + +[tool.hatch.version] +source = "code" +path = "src/towncrier/_version.py" +expression = "_hatchling_version" + +[tool.hatch.build] +exclude = [ + "admin", + "bin", + "docs", + ".readthedocs.yaml", + "src/towncrier/newsfragments", +] + + [tool.towncrier] package = "towncrier" package_dir = "src" @@ -77,14 +154,6 @@ module = 'incremental' ignore_missing_imports = true -[build-system] -requires = [ - "setuptools ~= 44.1.1", - "wheel ~= 0.36.2", - "incremental == 22.10.0", -] -build-backend = "setuptools.build_meta" - [tool.coverage.run] parallel = true branch = true diff --git a/setup.py b/setup.py deleted file mode 100644 index c6c11691..00000000 --- a/setup.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python - - -# If incremental is not present then setuptools just silently uses v0.0.0 so -# let's import it and fail instead. -import incremental # noqa - -from setuptools import find_packages, setup - - -setup( - name="towncrier", - url="https://github.com/twisted/towncrier", - project_urls={ - "Documentation": "https://towncrier.readthedocs.io/", - "Chat": "https://web.libera.chat/?channels=%23twisted", - "Mailing list": "https://mail.python.org/mailman3/lists/twisted.python.org/", - "Issues": "https://github.com/twisted/towncrier/issues", - "Repository": "https://github.com/twisted/towncrier", - "Tests": "https://github.com/twisted/towncrier/actions?query=branch%3Atrunk", - "Coverage": "https://codecov.io/gh/twisted/towncrier", - "Distribution": "https://pypi.org/project/towncrier", - }, - classifiers=[ - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Operating System :: POSIX :: Linux", - "Operating System :: MacOS :: MacOS X", - "Operating System :: Microsoft :: Windows", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", - ], - use_incremental=True, - python_requires=">=3.7", - install_requires=[ - "click", - "click-default-group", - "incremental", - "jinja2", - "setuptools", - "tomli; python_version<'3.11'", - ], - extras_require={ - "dev": [ - "packaging", - "sphinx >= 5", - "furo", - "twisted", - ], - }, - package_dir={"": "src"}, - packages=find_packages("src"), - license="MIT", - zip_safe=False, - include_package_data=True, - description="Building newsfiles for your project.", - long_description=open("README.rst").read(), - long_description_content_type="text/x-rst", - entry_points={"console_scripts": ["towncrier = towncrier._shell:cli"]}, -) diff --git a/src/towncrier/__init__.py b/src/towncrier/__init__.py index ab8ced7c..01448b29 100644 --- a/src/towncrier/__init__.py +++ b/src/towncrier/__init__.py @@ -7,7 +7,27 @@ from __future__ import annotations -from ._version import __version__ +from incremental import Version __all__ = ["__version__"] + + +def __getattr__(name: str) -> Version: + + if name != "__version__": + raise AttributeError(f"module {__name__} has no attribute {name}") + + import warnings + + from ._version import __version__ + + warnings.warn( + "Accessing towncrier.__version__ is deprecated and will be " + "removed in a future release. Use importlib.metadata directly " + "to query for towncrier's packaging metadata.", + DeprecationWarning, + stacklevel=2, + ) + + return __version__ diff --git a/src/towncrier/_version.py b/src/towncrier/_version.py index 40c39c27..a2529bf2 100644 --- a/src/towncrier/_version.py +++ b/src/towncrier/_version.py @@ -9,4 +9,8 @@ __version__ = Version("towncrier", 22, 12, 1, dev=0) -__all__ = ["__version__"] +# The version is exposed in string format to be +# available for the hatching build tools. +_hatchling_version = __version__.short() + +__all__ = ["__version__", "_hatchling_version"] diff --git a/src/towncrier/newsfragments/472.misc b/src/towncrier/newsfragments/472.misc new file mode 100644 index 00000000..e69de29b diff --git a/src/towncrier/test/test_packaging.py b/src/towncrier/test/test_packaging.py new file mode 100644 index 00000000..3355ad51 --- /dev/null +++ b/src/towncrier/test/test_packaging.py @@ -0,0 +1,22 @@ +# Copyright (c) Amber Brown, 2015 +# See LICENSE for details. + +from incremental import Version +from twisted.trial.unittest import TestCase + +from towncrier._version import _hatchling_version + + +class TestPackaging(TestCase): + def test_version_warning(self): + """ + Import __version__ from towncrier returns an Incremental version object + and raises a warning. + """ + with self.assertWarnsRegex( + DeprecationWarning, "Accessing towncrier.__version__ is deprecated.*" + ): + from towncrier import __version__ + + self.assertIsInstance(__version__, Version) + self.assertEqual(_hatchling_version, __version__.short()) diff --git a/src/towncrier/test/test_project.py b/src/towncrier/test/test_project.py index e55a3098..4b420648 100644 --- a/src/towncrier/test/test_project.py +++ b/src/towncrier/test/test_project.py @@ -55,8 +55,18 @@ def test_incremental(self): """ pkg = "../src" - self.assertEqual(metadata_version("towncrier"), get_version(pkg, "towncrier")) - self.assertEqual("towncrier", get_project_name(pkg, "towncrier")) + with self.assertWarnsRegex( + DeprecationWarning, "Accessing towncrier.__version__ is deprecated.*" + ): + version = get_version(pkg, "towncrier") + + with self.assertWarnsRegex( + DeprecationWarning, "Accessing towncrier.__version__ is deprecated.*" + ): + name = get_project_name(pkg, "towncrier") + + self.assertEqual(metadata_version("towncrier"), version) + self.assertEqual("towncrier", name) def _setup_missing(self): """