Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

♻️ Remove incremental #627

Merged
merged 15 commits into from
Jul 30, 2024
Merged
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ jobs:

- name: Install dependencies
run: |
python -m pip install --upgrade pip setuptools wheel
python -m pip install --upgrade pip wheel
python -m pip install pep517

- name: Display structure of files to be pushed
Expand Down
32 changes: 0 additions & 32 deletions admin/canonicalize_version.py

This file was deleted.

8 changes: 3 additions & 5 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,19 +55,17 @@
project = "Towncrier"
copyright = "{}, Towncrier contributors. Ver {}".format(
_today.year,
towncrier_version.public(),
towncrier_version,
)
author = "Amber Brown"

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
# The short X.Y version.
version = "{}.{}.{}".format(
towncrier_version.major, towncrier_version.minor, towncrier_version.micro
)
version = ".".join(towncrier_version.split(".")[:3])
# The full version, including alpha/beta/rc tags.
release = towncrier_version.public()
release = towncrier_version

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
Expand Down
11 changes: 2 additions & 9 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[build-system]
requires = [
"hatchling",
"incremental == 22.10.0",
"wheel",
]
build-backend = "hatchling.build"

Expand Down Expand Up @@ -32,8 +32,7 @@ dependencies = [
"click",
"importlib-resources>=5; python_version<'3.10'",
"importlib-metadata>=4.6; python_version<'3.10'",
"incremental",
"jinja2>=3",
"jinja2",
"tomli; python_version<'3.11'",
]

Expand Down Expand Up @@ -153,12 +152,6 @@ module = 'towncrier.click_default_group'
# Vendored module without type annotations.
ignore_errors = true

[[tool.mypy.overrides]]
module = 'incremental'
# No released version with type hints.
ignore_missing_imports = true


[tool.coverage.run]
parallel = true
branch = true
Expand Down
4 changes: 1 addition & 3 deletions src/towncrier/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@

from __future__ import annotations

from incremental import Version


__all__ = ["__version__"]


def __getattr__(name: str) -> Version:
def __getattr__(name: str) -> str:
if name != "__version__":
raise AttributeError(f"module {__name__} has no attribute {name}")

Expand Down
47 changes: 18 additions & 29 deletions src/towncrier/_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,12 @@

from __future__ import annotations

import contextlib
import sys

from importlib import import_module
from importlib.metadata import version as metadata_version
from types import ModuleType
from typing import Any

from incremental import Version as IncrementalVersion


if sys.version_info >= (3, 10):
Expand Down Expand Up @@ -63,17 +61,15 @@ def get_version(package_dir: str, package: str) -> str:
Try to extract the version from the distribution version metadata that matches
`package`, then fall back to looking for the package in `package_dir`.
"""
version: Any
version: str

# First try to get the version from the package metadata.
if version := _get_metadata_version(package):
return version

# When no version if found, fall back to looking for the package in `package_dir`.
module = _get_package(package_dir, package)

version = getattr(module, "__version__", None)

if not version:
raise Exception(
f"No __version__ or metadata version info for the '{package}' package."
Expand All @@ -82,37 +78,30 @@ def get_version(package_dir: str, package: str) -> str:
if isinstance(version, str):
return version.strip()

if isinstance(version, IncrementalVersion):
# FIXME:https://github.com/twisted/incremental/issues/81
# Incremental uses `.rcN`.
# importlib uses `rcN` (without a dot separation).
# Here we make incremental work like importlib.
return version.base().strip().replace(".rc", "rc")

if isinstance(version, tuple):
return ".".join(map(str, version)).strip()

# Try duck-typing as an Incremental version.
if hasattr(version, "base"):
try:
version = str(version.base()).strip()
# Incremental uses `X.Y.rcN`.
# Standardize on importlib (and PEP440) use of `X.YrcN`:
return version.replace(".rc", "rc") # type: ignore
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know why coverage is reporting this line as not covered.... we need to check

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was an indentation problem with a test, fixed now

except TypeError:
pass

raise Exception(
"I only know how to look at a __version__ that is a str, "
"an Increment Version, or a tuple. If you can't provide "
"that, use the --version argument and specify one."
"Version must be a string, tuple, or an Incremental Version."
" If you can't provide that, use the --version argument and specify one."
)


def get_project_name(package_dir: str, package: str) -> str:
module = _get_package(package_dir, package)

version = getattr(module, "__version__", None)
# Incremental has support for package names, try duck-typing it.
with contextlib.suppress(AttributeError):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we are missing a brach test here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added

Copy link
Member

@adiroiban adiroiban Jul 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still see this error reported

line 104 didn't return from function 'get_project_name' because the return on line 105 wasn't executed

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The miss indicator was a bit confusing for me, should be gone now hopefully

return str(version.package) # type: ignore

if not version:
# welp idk
return package.title()

if isinstance(version, str):
return package.title()

if isinstance(version, IncrementalVersion):
# Incremental has support for package names
return version.package

raise TypeError(f"Unsupported type for __version__: {type(version)}")
return package.title()
2 changes: 1 addition & 1 deletion src/towncrier/_shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@


@click.group(cls=DefaultGroup, default="build", default_if_no_args=True)
@click.version_option(__version__.public())
@click.version_option(__version__)
def cli() -> None:
"""
Towncrier is a utility to produce useful, summarised news files for your project.
Expand Down
23 changes: 8 additions & 15 deletions src/towncrier/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,11 @@
Provides towncrier version information.
"""

# This file is auto-generated! Do not edit!
# Use `python -m incremental.update towncrier` to change this file.

from incremental import Version


# For dev - Version('towncrier', 23, 8, 1, dev=0)
# For RC - Version('towncrier', 23, 9, 0, release_candidate=1)
# For final - Version('towncrier', 23, 9, 0)
__version__ = Version("towncrier", 23, 11, 1, dev=0)
# The version is exposed in string format to be
# available for the hatching build tools.
_hatchling_version = __version__.short()

__all__ = ["__version__", "_hatchling_version"]
# For dev - 23.11.1.dev0
# For RC - 23.11.1.rc1
# For final - 23.11.1
# make sure to follow PEP440
__version__ = "23.11.1.dev0"

_hatchling_version = __version__
__all__ = ["_hatchling_version"]
3 changes: 3 additions & 0 deletions src/towncrier/newsfragments/491.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Remove incremental dependency from towncrier.

Towncrier can still read incremental versions, it just doesn't rely on the package itself any more.
26 changes: 15 additions & 11 deletions src/towncrier/test/test_packaging.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
# 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
import towncrier


class TestPackaging(TestCase):
def test_version_warning(self):
def test_version_attr(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure why this change is needed.

The previous test look good enough to me. :)

I am misssing something ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous test was checking for the presence of an Incremental Version object (which obviously no longer exists in towncrier)

I've replaced it with a test to assert that a DeprecationWarning is present

"""
Import __version__ from towncrier returns an Incremental version object
and raises a warning.
towncrier.__version__ was deprecated, but still exists for now.
"""
with self.assertWarnsRegex(
DeprecationWarning, "Accessing towncrier.__version__ is deprecated.*"
):
from towncrier import __version__

self.assertIsInstance(__version__, Version)
self.assertEqual(_hatchling_version, __version__.short())
def access__version():
return towncrier.__version__

expected_warning = (
"Accessing towncrier.__version__ is deprecated and will be "
"removed in a future release. Use importlib.metadata directly "
"to query for towncrier's packaging metadata."
)

self.assertWarns(
DeprecationWarning, expected_warning, __file__, access__version
)
76 changes: 54 additions & 22 deletions src/towncrier/test/test_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import sys

from importlib.metadata import version as metadata_version
from unittest import mock

from click.testing import CliRunner
from twisted.trial.unittest import TestCase
Expand Down Expand Up @@ -45,32 +44,65 @@ def test_tuple(self):
version = get_version(temp, "mytestproja")
self.assertEqual(version, "1.3.12")

def test_incremental(self):
def test_incremental(self):
"""
An incremental-like Version __version__ is picked up.
"""
temp = self.mktemp()
os.makedirs(temp)
os.makedirs(os.path.join(temp, "mytestprojinc"))

with open(os.path.join(temp, "mytestprojinc", "__init__.py"), "w") as f:
f.write(
"""
class Version:
'''
This is emulating a Version object from incremental.
'''

def __init__(self, *version_parts):
self.version = version_parts

def base(self):
return '.'.join(map(str, self.version))


__version__ = Version(1, 3, 12, "rc1")
"""
)
version = get_version(temp, "mytestprojinc")
self.assertEqual(version, "1.3.12rc1")

def test_not_incremental(self):
"""
An incremental Version __version__ is picked up.
An incremental-like Version __version__ is picked up, base takes unexpected
arguments.
sigma67 marked this conversation as resolved.
Show resolved Hide resolved
"""
pkg = "../src"
temp = self.mktemp()
os.makedirs(os.path.join(temp, "mytestprojnotinc"))

with self.assertWarnsRegex(
DeprecationWarning, "Accessing towncrier.__version__ is deprecated.*"
):
# Previously this triggered towncrier.__version__ but now the first version
# check is from the package metadata. Let's mock out that part to ensure we
# can get incremental versions from __version__ still.
with mock.patch(
"towncrier._project._get_metadata_version", return_value=None
):
version = get_version(pkg, "towncrier")
with open(os.path.join(temp, "mytestprojnotinc", "__init__.py"), "w") as f:
f.write(
"""
class WeirdVersion:
def base(self, some_arg):
return "shouldn't get here"

version = get_version(pkg, "towncrier")

with self.assertWarnsRegex(
DeprecationWarning, "Accessing towncrier.__version__ is deprecated.*"
):
name = get_project_name(pkg, "towncrier")
__version__ = WeirdVersion()
"""
)
with self.assertRaises(Exception) as e:
get_version(temp, "mytestprojnotinc")

self.assertEqual(metadata_version("towncrier"), version)
self.assertEqual("towncrier", name)
self.assertEqual(
(
"Version must be a string, tuple, or an Incremental Version. "
"If you can't provide that, use the --version argument and "
"specify one.",
),
e.exception.args,
)

def test_version_from_metadata(self):
"""
Expand Down Expand Up @@ -129,7 +161,7 @@ def test_unknown_type(self):

self.assertRaises(Exception, get_version, temp, "mytestprojb")

self.assertRaises(TypeError, get_project_name, temp, "mytestprojb")
self.assertEqual("Mytestprojb", get_project_name(temp, "mytestprojb"))

def test_import_fails(self):
"""
Expand Down
Loading