From e46add820ce641250553387bf5f62e48d41ac898 Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Tue, 21 Feb 2023 00:39:08 -0500 Subject: [PATCH 1/6] build: migrate build backend to hatchling --- .github/workflows/lint-test.yml | 1 + MANIFEST.in | 7 ------ noxfile.py | 13 +++-------- pyproject.toml | 38 ++++++++++----------------------- setup.py | 37 -------------------------------- 5 files changed, 15 insertions(+), 81 deletions(-) delete mode 100644 MANIFEST.in delete mode 100644 setup.py diff --git a/.github/workflows/lint-test.yml b/.github/workflows/lint-test.yml index 7c8d602f4b..d294f81c71 100644 --- a/.github/workflows/lint-test.yml +++ b/.github/workflows/lint-test.yml @@ -256,6 +256,7 @@ jobs: run: | nox -s slotscheck + # This only runs if the previous steps were successful, no point in running it otherwise - name: Try building package if: (success() || failure()) && steps.setup.outcome == 'success' run: | diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 8f98414e11..0000000000 --- a/MANIFEST.in +++ /dev/null @@ -1,7 +0,0 @@ -include disnake/bin/* -include disnake/py.typed -include disnake/ext/commands/py.typed -include disnake/ext/tasks/py.typed -global-exclude *.py[cod] -exclude uv.lock -exclude noxfile.py diff --git a/noxfile.py b/noxfile.py index 4062de2c7a..2469f63553 100755 --- a/noxfile.py +++ b/noxfile.py @@ -74,7 +74,7 @@ def __post_init__(self) -> None: ExecutionGroup( sessions=("pyright",), python=python, - pyright_paths=("disnake", "tests", "examples", "noxfile.py", "setup.py"), + pyright_paths=("disnake", "tests", "examples", "noxfile.py"), project=True, extras=("speed", "voice"), groups=("test", "nox"), @@ -96,7 +96,7 @@ def __post_init__(self) -> None: ), # the other sessions, they don't need pyright, but they need to run ExecutionGroup( - sessions=("lint", "slotscheck", "check-manifest"), + sessions=("lint", "slotscheck"), groups=("tools",), ), # build @@ -250,13 +250,6 @@ def lint(session: nox.Session) -> None: session.run("prek", "run", "--all-files", *session.posargs) -@nox.session(name="check-manifest") -def check_manifest(session: nox.Session) -> None: - """Run check-manifest.""" - install_deps(session) - session.run("check-manifest", "-v") - - @nox.session(python=get_version_for_session("slotscheck")) def slotscheck(session: nox.Session) -> None: """Run slotscheck.""" @@ -264,7 +257,7 @@ def slotscheck(session: nox.Session) -> None: session.run("python", "-m", "slotscheck", "--verbose", "-m", "disnake") -@nox.session(requires=["check-manifest"]) +@nox.session def build(session: nox.Session) -> None: """Build a dist.""" install_deps(session) diff --git a/pyproject.toml b/pyproject.toml index e2d5add486..7acabe3f52 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: MIT [build-system] -requires = ["setuptools>=77.0.3"] -build-backend = "setuptools.build_meta" +requires = ["hatchling"] +build-backend = "hatchling.build" [project] name = "disnake" @@ -84,7 +84,6 @@ ruff = [ tools = [ "prek>=0.2.0", "slotscheck==0.19.1", - "check-manifest==0.50", { include-group = "ruff" }, ] changelog = [ @@ -114,13 +113,18 @@ build = [ "twine>=6.1.0", ] -[tool.setuptools.packages.find] -where = ["."] -include = ["disnake*"] - [tool.uv] required-version = ">=0.9.2" +[tool.hatch.build.targets.sdist] +only-include = [ + "disnake", +] + +[tool.hatch.version] +path = "disnake/__init__.py" +pattern = '__version__ = "(?P.+)"' + [tool.ruff] line-length = 100 @@ -415,23 +419,3 @@ exclude_lines = [ "^\\s*raise NotImplementedError$", "^\\s*\\.\\.\\.$", ] - - -[tool.check-manifest] -ignore = [ - # CI - ".pre-commit-config.yaml", - ".readthedocs.yml", - ".libcst.codemod.yaml", - "noxfile.py", - # docs - "CONTRIBUTING.md", - "RELEASE.md", - "assets/**", - "changelog/**", - "docs/**", - "examples/**", - # tests - "tests/**", - "scripts/**", -] diff --git a/setup.py b/setup.py deleted file mode 100644 index bd16e72006..0000000000 --- a/setup.py +++ /dev/null @@ -1,37 +0,0 @@ -# SPDX-License-Identifier: MIT - -import re - -from setuptools import setup - -version = "" -with open("disnake/__init__.py", encoding="utf-8") as f: - version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', f.read(), re.MULTILINE).group(1) # pyright: ignore[reportOptionalMemberAccess] - -if not version: - msg = "version is not set" - raise RuntimeError(msg) - -if version.endswith(("a", "b", "rc")): - # append version identifier based on commit count - try: - import subprocess # noqa: TID251 - - p = subprocess.Popen( - ["git", "rev-list", "--count", "HEAD"], stdout=subprocess.PIPE, stderr=subprocess.PIPE - ) - out, err = p.communicate() - if out: - version += out.decode("utf-8").strip() - p = subprocess.Popen( - ["git", "rev-parse", "--short", "HEAD"], stdout=subprocess.PIPE, stderr=subprocess.PIPE - ) - out, err = p.communicate() - if out: - version += "+g" + out.decode("utf-8").strip() - except Exception: - pass - -setup( - version=version, -) From d82d10cdb72fd81e872f3e66d67f740e85e854d4 Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Sat, 23 Aug 2025 19:34:17 -0400 Subject: [PATCH 2/6] build: add dynamic versioning support using versioningit for storing version info as tags --- .gitattributes | 6 ++--- .github/workflows/lint-test.yml | 2 ++ .github/workflows/release.yaml | 1 + .gitignore | 1 + .readthedocs.yml | 2 ++ disnake/__init__.py | 20 ++++---------- disnake/_version.pyi | 14 ++++++++++ noxfile.py | 7 ++--- pyproject.toml | 48 ++++++++++++++++++++++++++++++--- 9 files changed, 77 insertions(+), 24 deletions(-) create mode 100644 disnake/_version.pyi diff --git a/.gitattributes b/.gitattributes index 4630853702..430abc6838 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,3 @@ -# force LF for pyproject to make hashFiles in CI consistent (windows <3) -# (see https://github.com/actions/runner/issues/498) -pyproject.toml text eol=lf +# - `eol=lf` to make hashFiles in CI consistent (windows <3) (see https://github.com/actions/runner/issues/498) +# - `export-subst` makes git expand/replace the `describe-subst` field value when creating an archive +pyproject.toml text eol=lf export-subst diff --git a/.github/workflows/lint-test.yml b/.github/workflows/lint-test.yml index d294f81c71..b71856cce6 100644 --- a/.github/workflows/lint-test.yml +++ b/.github/workflows/lint-test.yml @@ -156,6 +156,7 @@ jobs: steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: + fetch-depth: '0' persist-credentials: false - name: Set up environment @@ -243,6 +244,7 @@ jobs: steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: + fetch-depth: '0' persist-credentials: false - name: Set up environment diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index ea10ded597..4a79e54334 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -19,6 +19,7 @@ jobs: steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: + fetch-depth: '0' persist-credentials: false - name: Set up environment diff --git a/.gitignore b/.gitignore index 3c66053d3d..b69ed18466 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ coverage.xml __pypackages__/ .python-version uv.lock +disnake/_version.py diff --git a/.readthedocs.yml b/.readthedocs.yml index f20bba070c..cd1558d7bb 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -15,6 +15,8 @@ build: - UV_PROJECT_ENVIRONMENT=$READTHEDOCS_VIRTUALENV_PATH uv sync --no-default-groups --extra docs install: - "true" + post_checkout: + - git fetch --tags || true sphinx: configuration: docs/conf.py diff --git a/disnake/__init__.py b/disnake/__init__.py index 92eae6bec7..a674f5003f 100644 --- a/disnake/__init__.py +++ b/disnake/__init__.py @@ -14,14 +14,17 @@ __author__ = "Rapptz, EQUENOS" __license__ = "MIT" __copyright__ = "Copyright 2015-present Rapptz, 2021-present EQUENOS" -__version__ = "2.12.0a" __path__ = __import__("pkgutil").extend_path(__path__, __name__) import logging -from typing import Literal, NamedTuple from . import abc as abc, opus as opus, ui as ui, utils as utils # explicitly re-export modules +from ._version import ( + VersionInfo as VersionInfo, + __version__ as __version__, + version_info as version_info, +) from .activity import * from .app_commands import * from .appinfo import * @@ -77,17 +80,4 @@ from .welcome_screen import * from .widget import * - -class VersionInfo(NamedTuple): - major: int - minor: int - micro: int - releaselevel: Literal["alpha", "beta", "candidate", "final"] - serial: int - - -# fmt: off -version_info: VersionInfo = VersionInfo(major=2, minor=12, micro=0, releaselevel="alpha", serial=0) -# fmt: on - logging.getLogger(__name__).addHandler(logging.NullHandler()) diff --git a/disnake/_version.pyi b/disnake/_version.pyi new file mode 100644 index 0000000000..a935e0ebb4 --- /dev/null +++ b/disnake/_version.pyi @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: MIT + +from typing import Literal, NamedTuple + +__version__: str + +class VersionInfo(NamedTuple): + major: int + minor: int + micro: int + releaselevel: Literal["alpha", "beta", "candidate", "final"] + serial: int + +version_info: VersionInfo diff --git a/noxfile.py b/noxfile.py index 2469f63553..641aac7953 100755 --- a/noxfile.py +++ b/noxfile.py @@ -78,7 +78,7 @@ def __post_init__(self) -> None: project=True, extras=("speed", "voice"), groups=("test", "nox"), - dependencies=("setuptools", "pytz", "requests"), # needed for type checking + dependencies=("pytz", "requests"), # needed for type checking ) for python in ALL_PYTHONS ), @@ -91,7 +91,7 @@ def __post_init__(self) -> None: # codemodding and pyright ExecutionGroup( sessions=("codemod", "autotyping", "pyright"), - pyright_paths=("scripts",), + pyright_paths=("scripts/codemods", "scripts/ci"), groups=("codemod",), ), # the other sessions, they don't need pyright, but they need to run @@ -101,7 +101,8 @@ def __post_init__(self) -> None: ), # build ExecutionGroup( - sessions=("build",), + sessions=("build", "pyright"), + pyright_paths=("scripts/versioning.py",), groups=("build",), ), ## testing diff --git a/pyproject.toml b/pyproject.toml index 7acabe3f52..1b0008ed83 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: MIT [build-system] -requires = ["hatchling"] +requires = ["hatchling>=1.27.0", "versioningit>=3.3.0,<4"] build-backend = "hatchling.build" [project] @@ -62,6 +62,7 @@ docs = [ "towncrier==23.6.0", "sphinx-notfound-page==0.8.3", "sphinxext-opengraph==0.9.1", + "versioningit>=3.3.0,<4", ] [dependency-groups] @@ -111,19 +112,60 @@ test = [ build = [ "build>=1.2.2.post1", "twine>=6.1.0", + "versioningit>=3.3.0,<4", ] [tool.uv] required-version = ">=0.9.2" +[tool.hatch.build] +artifacts = ["disnake/_version.py"] + [tool.hatch.build.targets.sdist] only-include = [ "disnake", ] [tool.hatch.version] -path = "disnake/__init__.py" -pattern = '__version__ = "(?P.+)"' +source = "versioningit" + +[tool.versioningit.vcs] +method = "git-archive" +describe-subst = "$Format:%(describe:tags,match=v*)$" + +[tool.versioningit.format] +distance = "{base_version}a{distance}.{vcs}{rev}" +dirty = "{base_version}+d{build_date:%Y%m%d}" +distance-dirty = "{base_version}a{distance}+{vcs}{rev}.d{build_date:%Y%m%d}" + +[tool.versioningit.template-fields.version-tuple] +split-on = '^(\d+)\.(\d+)\.(\d+)\.([a-zA-Z])(\d+)(?:.\d+)?' + + +[tool.versioningit.write] +file = "disnake/_version.py" +template = """ +# SPDX-License-Identifier: MIT + +from typing import Literal, NamedTuple + +__version__ = "{version}" + + +class VersionInfo(NamedTuple): + major: int + minor: int + micro: int + releaselevel: Literal["alpha", "beta", "candidate", "final"] + serial: int + + +# fmt: off +version_info: VersionInfo = VersionInfo(*{version_tuple}[:5]) +# fmt: on + +""" + [tool.ruff] line-length = 100 From 39ac91be395c3b13ff089a46cc5a4f75f5de5744 Mon Sep 17 00:00:00 2001 From: arielle Date: Tue, 26 Aug 2025 22:10:59 -0400 Subject: [PATCH 3/6] fix: restore version_info attribute and VersionInfo tuple --- pyproject.toml | 14 ++++------- scripts/versioning.py | 56 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 scripts/versioning.py diff --git a/pyproject.toml b/pyproject.toml index 1b0008ed83..617ea11f3e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -124,6 +124,7 @@ artifacts = ["disnake/_version.py"] [tool.hatch.build.targets.sdist] only-include = [ "disnake", + "scripts/versioning.py", ] [tool.hatch.version] @@ -134,13 +135,12 @@ method = "git-archive" describe-subst = "$Format:%(describe:tags,match=v*)$" [tool.versioningit.format] -distance = "{base_version}a{distance}.{vcs}{rev}" +distance = "{base_version}a{distance}+{vcs}{rev}" dirty = "{base_version}+d{build_date:%Y%m%d}" distance-dirty = "{base_version}a{distance}+{vcs}{rev}.d{build_date:%Y%m%d}" -[tool.versioningit.template-fields.version-tuple] -split-on = '^(\d+)\.(\d+)\.(\d+)\.([a-zA-Z])(\d+)(?:.\d+)?' - +[tool.versioningit.template-fields] +method = { module = "scripts.versioning", value = "template_fields"} [tool.versioningit.write] file = "disnake/_version.py" @@ -159,11 +159,7 @@ class VersionInfo(NamedTuple): releaselevel: Literal["alpha", "beta", "candidate", "final"] serial: int - -# fmt: off -version_info: VersionInfo = VersionInfo(*{version_tuple}[:5]) -# fmt: on - +version_info: VersionInfo = VersionInfo{version_tuple} """ diff --git a/scripts/versioning.py b/scripts/versioning.py new file mode 100644 index 0000000000..d77f9b7c63 --- /dev/null +++ b/scripts/versioning.py @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: MIT + +from typing import Any, Optional + +import packaging.version +import versioningit + + +def template_fields( + *, + version: str, + description: Optional[versioningit.VCSDescription], + base_version: Optional[str], + next_version: Optional[str], + params: dict[str, Any], +) -> dict[str, Any]: + """Implements the ``"basic"`` ``template-fields`` method""" + # params = copy.deepcopy(params) + parsed_version = packaging.version.parse(version) + fields: dict[str, Any] = {} + if description is not None: + fields.update(description.fields) + fields["branch"] = description.branch + if base_version is not None: + fields["base_version"] = base_version + if next_version is not None: + fields["next_version"] = next_version + fields["version"] = version + + releaselevels = { + "a": "alpha", + "b": "beta", + "rc": "candidate", + "": "final", + } + + if parsed_version.pre: + pre = parsed_version.pre + releaselevel = releaselevels.get(pre[0], "final") + serial = pre[1] + else: + releaselevel = "final" + serial = 0 + + fields["version_tuple"] = ( + parsed_version.major, + parsed_version.minor, + parsed_version.micro, + releaselevel, + serial, + ) + try: + fields["normalized_version"] = str(packaging.version.Version(version)) + except ValueError: + fields["normalized_version"] = version + return fields From 87d6b0f7ee290c3a3288bdc8e8c9884bd5f1caf1 Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Sun, 19 Oct 2025 18:37:45 -0400 Subject: [PATCH 4/6] fix: use the next version for docs generation --- .readthedocs.yml | 10 +++++++++- docs/conf.py | 10 +++++++++- docs/extensions/versionchange.py | 5 +++-- docs/whats_new.rst | 2 +- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index cd1558d7bb..3bd897e1a2 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -16,7 +16,15 @@ build: install: - "true" post_checkout: - - git fetch --tags || true + - | + while true; do + TAG=$(git tag --merged HEAD --sort=-committerdate 'v*' | head -n 1) + if [ -n "$TAG" ]; then + echo "✅ Found first reachable tag: $TAG" + break + fi + git fetch --deepen=50 + done sphinx: configuration: docs/conf.py diff --git a/docs/conf.py b/docs/conf.py index bdbf66be34..39e7d2838c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -21,6 +21,7 @@ import sys from typing import Any, Optional +import versioningit from sphinx.application import Sphinx # If extensions (or modules to document with autodoc) are in another directory, @@ -109,9 +110,16 @@ # # The full version, including alpha/beta/rc tags. release = importlib.metadata.version("disnake") - # The short X.Y version. version = ".".join(release.split(".")[:2]) +# The release for the next release +next_release = versioningit.get_next_version(os.path.abspath("..")) +next_version = ".".join((next_release).split(".", 2)[:2]) + +rst_prolog += f""" +.. |vnext_full| replace:: {next_release} +.. |vnext| replace:: {next_version} +""" _IS_READTHEDOCS = bool(os.getenv("READTHEDOCS")) diff --git a/docs/extensions/versionchange.py b/docs/extensions/versionchange.py index 49c66a95cf..1239b14c2a 100644 --- a/docs/extensions/versionchange.py +++ b/docs/extensions/versionchange.py @@ -18,8 +18,7 @@ def run(self): # If the argument is |vnext|, replace with config version if self.arguments and self.arguments[0] == "|vnext|": # Get the version from the Sphinx config - version = self.env.config.version - self.arguments[0] = version + self.arguments[0] = self.env.config.next_version return super().run() @@ -28,6 +27,8 @@ def setup(app: Sphinx) -> SphinxExtensionMeta: app.add_directive("versionchanged", VersionAddedNext, override=True) app.add_directive("deprecated", VersionAddedNext, override=True) + app.add_config_value("next_version", None, "env", types=[str]) + return { "parallel_read_safe": True, "parallel_write_safe": True, diff --git a/docs/whats_new.rst b/docs/whats_new.rst index 3f0cafccaf..d702658b9f 100644 --- a/docs/whats_new.rst +++ b/docs/whats_new.rst @@ -13,7 +13,7 @@ Changelog This page keeps a detailed human friendly rendering of what's new and changed in specific versions. Please see :ref:`version_guarantees` for more information. -.. towncrier-draft-entries:: |release| [UNRELEASED] +.. towncrier-draft-entries:: |vnext_full| [UNRELEASED] .. towncrier release notes start From 9ad626d32e00fae946916b82cf40409b2a68f019 Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Mon, 25 Aug 2025 17:44:05 -0400 Subject: [PATCH 5/6] fix: provide a fallback version when the version cannot be determined Without this setting, installation of disnake from a source distribution without source control metadata will fail, because there will be no way to determine the version of disnake without that metadata. Using a fallback ensures that in such an installation method (such as from a GitHub source archive), disnake can still be installed, though the version metadata may be inaccurate. Compared to not allowing installation at all, this is a better user experience, despite the incorrect version information. --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 617ea11f3e..5e1fb3c070 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -130,6 +130,9 @@ only-include = [ [tool.hatch.version] source = "versioningit" +[tool.versioningit] +default-version = "0.0.0" + [tool.versioningit.vcs] method = "git-archive" describe-subst = "$Format:%(describe:tags,match=v*)$" From a4ed52b76990fd9d53fe843b9362802fc9086398 Mon Sep 17 00:00:00 2001 From: arielle Date: Tue, 26 Aug 2025 22:56:53 -0400 Subject: [PATCH 6/6] docs: add changelog --- changelog/1323.misc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/1323.misc.rst diff --git a/changelog/1323.misc.rst b/changelog/1323.misc.rst new file mode 100644 index 0000000000..d6b1d715ff --- /dev/null +++ b/changelog/1323.misc.rst @@ -0,0 +1 @@ +Use hatchling and versioningit for building disnake rather than using setuptools.