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

Modernize development infrastructure #177

Merged
merged 28 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
7bb6de3
Move setuptools config to pyproject.toml
SethMMorton Oct 7, 2024
7028e5a
Move mypy configuration to pyproject.toml
SethMMorton Oct 8, 2024
7737a68
Move flake8 config to pyproject.toml
SethMMorton Oct 8, 2024
fee2006
Auto-formatting
SethMMorton Oct 8, 2024
6db6d39
Use ruff format instead of black
SethMMorton Oct 8, 2024
211212a
Move linting from flake8 to ruff check
SethMMorton Oct 8, 2024
d16790e
Add pyupgrade checks
SethMMorton Oct 8, 2024
bc5398a
Add flake8-2020 linting
SethMMorton Oct 8, 2024
f2b075e
Add flake8-commas
SethMMorton Oct 8, 2024
405adce
Add flake8-comprehensions check
SethMMorton Oct 8, 2024
9bd1692
Add flake8-errmsg check
SethMMorton Oct 8, 2024
89594e1
Add flake8-pyi check
SethMMorton Oct 8, 2024
4915c05
Add flake8-pytest-style
SethMMorton Oct 8, 2024
65b1078
Add flake8-return and flake8-raise checks
SethMMorton Oct 8, 2024
e4e0461
Add flake8-simplify and flake8-unused-arguments
SethMMorton Oct 8, 2024
cbf42fa
Add eradicate check
SethMMorton Oct 8, 2024
4f73187
Add ruff-specific-rules checks
SethMMorton Oct 8, 2024
f1598b3
Address ruff check and format conflicts
SethMMorton Oct 8, 2024
bba9ddc
Update list of cleanables
SethMMorton Oct 9, 2024
98588fe
Use setuptools_scm instead of bumpversion
SethMMorton Oct 9, 2024
2ed418e
Remove release env from tox.ini
SethMMorton Oct 9, 2024
a123d8f
Update CI to use ruff and modern building
SethMMorton Oct 9, 2024
56ed975
auto-formatting
SethMMorton Oct 9, 2024
8419a39
Use correct invocation of ruff action
SethMMorton Oct 9, 2024
e91e2f1
Drop Python 3.7 support
SethMMorton Oct 9, 2024
46484e4
Fix issues around new _version.py file
SethMMorton Oct 9, 2024
bef9e89
Update documentation to reflect new config files
SethMMorton Oct 10, 2024
5ac16f2
Update changelog
SethMMorton Oct 10, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 12 additions & 14 deletions .github/workflows/code-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,10 @@ jobs:
with:
python-version: '3.8'

- name: Install black
run: pip install black

- name: Run black
run: black --quiet --check --diff .
- name: Run ruff
uses: astral-sh/ruff-action@v1
with:
args: 'format --check'

static-analysis:
name: Static Analysis
Expand All @@ -40,11 +39,8 @@ jobs:
with:
python-version: '3.8'

- name: Install Flake8
run: pip install flake8 flake8-import-order flake8-bugbear pep8-naming

- name: Run Flake8
run: flake8
- name: Run ruff
uses: astral-sh/ruff-action@v1

type-checking:
name: Type Checking
Expand All @@ -59,7 +55,10 @@ jobs:
python-version: '3.8'

- name: Install MyPy
run: pip install mypy hypothesis pytest pytest-mock fastnumbers
run: pip install mypy hypothesis pytest pytest-mock fastnumbers setuptools_scm

- name: Create _version.py file
run: python -m setuptools_scm --force-write-version-files

- name: Run MyPy
run: mypy --strict natsort tests
Expand All @@ -77,10 +76,9 @@ jobs:
python-version: '3.8'

- name: Install Validators
run: pip install twine check-manifest
run: pip install twine build

- name: Run Validation
run: |
check-manifest --ignore ".github*,*.md,.coveragerc"
python setup.py sdist
python -m build
twine check dist/*
5 changes: 2 additions & 3 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@ jobs:

- name: Build Source Distribution and Wheel
run: |
pip install wheel
python setup.py sdist --format=gztar
pip wheel . -w dist
pip install build
python -m build

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: [3.7, 3.8, 3.9, "3.10", "3.11"]
python-version: [3.8, 3.9, "3.10", "3.11", "3.12"]
os: [ubuntu-latest]
extras: [false]
include:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
*.py[co]
_version.py

# Packages
*.egg
Expand Down
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
Unreleased
---

### Changed

- Modernize development infrastructure
(PR [#162](https://github.com/SethMMorton/natsort/issues/177))
- Use `ruff` instead of `flake8` and `black`
- Move all config to `pyproject.toml`
- Use `setuptools-scm` to track `__version__`
- Remove `bumpversion` for creating new releases

### Fixed

- Eliminated mypy failure related to string literals

### Removed

- Support for EOL Python 3.7

[8.4.0] - 2023-06-19
---

Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ a good reason not to be).
Located in the `dev/` folder is development collateral such as formatting and
patching scripts. The only development collateral not in the `dev/`
folder are those files that are expected to exist in the the top-level directory
(such as `setup.py`, `tox.ini`, and CI configuration). All of these scripts
(such as `pyproject.toml`, `tox.ini`, and CI configuration). All of these scripts
can either be run with the python stdandard library, or have hooks in `tox`.

I do not have strong opinions on how one should contribute, so
Expand Down
10 changes: 0 additions & 10 deletions MANIFEST.in

This file was deleted.

2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ from the command line with ``python -m natsort``. Check out the
Requirements
------------

`natsort`_ requires Python 3.7 or greater.
`natsort`_ requires Python 3.8 or greater.

Optional Dependencies
---------------------
Expand Down
72 changes: 44 additions & 28 deletions dev/bump.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,24 @@
INTENDED TO BE CALLED FROM PROJECT ROOT, NOT FROM dev/!
"""

import datetime
import subprocess
import sys

from setuptools_scm import get_version

# Ensure a clean repo before moving on.
ret = subprocess.run(
["git", "status", "--porcelain", "--untracked-files=no"],
check=True,
capture_output=True,
text=True,
)
if ret.stdout:
sys.exit("Cannot bump unless the git repo has no changes.")


# A valid bump must have been given.
try:
bump_type = sys.argv[1]
except IndexError:
Expand All @@ -28,44 +43,45 @@ def git(cmd, *args):
sys.exit(e.returncode)


def bumpversion(severity, *args, catch=False):
"""Wrapper for calling bumpversion"""
cmd = ["bump2version", *args, severity]
try:
if catch:
return subprocess.run(
cmd, check=True, capture_output=True, text=True
).stdout
else:
subprocess.run(cmd, check=True, text=True)
except subprocess.CalledProcessError as e:
print("Call to bump2version failed!", file=sys.stderr)
print("STDOUT:", e.stdout, file=sys.stderr)
print("STDERR:", e.stderr, file=sys.stderr)
sys.exit(e.returncode)
# Use setuptools-scm to identify the current version
current_version = get_version(
root="..",
relative_to=__file__,
local_scheme="no-local-version",
version_scheme="only-version",
)

# Increment the version according to the bump type
version_components = list(map(int, current_version.split(".")))
incr_index = {"major": 0, "minor": 1, "patch": 2}[bump_type]
version_components[incr_index] += 1
for i in range(incr_index + 1, 3):
version_components[i] = 0
next_version = ".".join(map(str, version_components))

# Do a dry run of the bump to find what the current version is and what it will become.
data = bumpversion(bump_type, "--dry-run", "--list", catch=True)
data = dict(x.split("=") for x in data.splitlines())
# Update the changelog.
with open("CHANGELOG.md") as fl:
changelog = fl.read()

# Execute the bumpversion.
bumpversion(bump_type)
# Add a date to this entry.
changelog = changelog.replace(
"Unreleased",
f"Unreleased\n---\n\n[{next_version}] - {datetime.datetime.now():%Y-%m-%d}",
)

# Post-process the changelog with things that bumpversion is not good at updating.
with open("CHANGELOG.md") as fl:
changelog = fl.read().replace(
# Add links to the entries.
changelog = changelog.replace(
"<!---Comparison links-->",
"<!---Comparison links-->\n[{new}]: {url}/{current}...{new}".format(
new=data["new_version"],
current=data["current_version"],
new=next_version,
current=current_version,
url="https://github.com/SethMMorton/natsort/compare",
),
)
with open("CHANGELOG.md", "w") as fl:
fl.write(changelog)

# Finally, add the CHANGELOG.md changes to the previous commit.
# Add the CHANGELOG.md changes and commit & tag.
git("add", "CHANGELOG.md")
git("commit", "--amend", "--no-edit")
git("tag", "--force", data["new_version"], "HEAD")
git("commit", "--message", f"Bump version: {current_version} → {next_version}")
git("tag", next_version, "HEAD")
9 changes: 9 additions & 0 deletions dev/clean.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@
pathlib.Path("build"),
pathlib.Path("dist"),
pathlib.Path(".pytest_cache"),
pathlib.Path(".ruff_cache"),
pathlib.Path(".mypy_cache"),
pathlib.Path(".pytest_cache"),
pathlib.Path(".hypothesis"),
pathlib.Path(".tox"),
pathlib.Path(".coverage"),
]
dirs += pathlib.Path.cwd().glob("*.egg-info")
for d in dirs:
Expand All @@ -30,3 +34,8 @@
# Shouldn't be any .pyc left, but just in case
for f in pathlib.Path.cwd().rglob("*.pyc"):
f.unlink()

# Remove _version.py
version = pathlib.Path("natsort/_version.py")
if version.is_file():
version.unlink()
4 changes: 2 additions & 2 deletions dev/generate_new_unicode_numbers.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
"""
Generate the numeric hex list of unicode numerals
"""

import os
import os.path
import sys
Expand Down Expand Up @@ -40,6 +40,6 @@
if a in "0123456789":
continue
if unicodedata.numeric(a, None) is not None:
print(" 0x{:X},".format(i), file=fl)
print(f" 0x{i:X},", file=fl)

print(")", file=fl)
4 changes: 3 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

import os

import natsort

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
Expand Down Expand Up @@ -58,7 +60,7 @@
# built documents.
#
# The full version, including alpha/beta/rc tags.
release = "8.4.0"
release = natsort.__version__
# The short X.Y version.
version = ".".join(release.split(".")[0:2])

Expand Down
13 changes: 8 additions & 5 deletions natsort/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# -*- coding: utf-8 -*-

try:
# The redundant "as" tells mypy to treat as explict import
from natsort._version import __version__ as __version__
from natsort._version import __version_tuple__ as __version_tuple__
except ImportError:
__version__ = "unknown version"
__version_tuple__ = (0, 0, "unknown version")
from natsort.natsort import (
NatsortKeyType,
OSSortKeyType,
Expand All @@ -23,8 +28,6 @@
from natsort.ns_enum import NSType, ns
from natsort.utils import KeyType, NatsortInType, NatsortOutType, chain_functions

__version__ = "8.4.0"

__all__ = [
"natsort_key",
"natsort_keygen",
Expand Down Expand Up @@ -53,4 +56,4 @@
]

# Add the ns keys to this namespace for convenience.
globals().update({name: value for name, value in ns.__members__.items()})
globals().update(dict(ns.__members__.items()))
17 changes: 9 additions & 8 deletions natsort/__main__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-

import argparse
import sys
from typing import Callable, Iterable, List, Optional, Pattern, Tuple, Union, cast
Expand Down Expand Up @@ -64,7 +62,7 @@ def main(*arguments: str) -> None:
parser.add_argument(
"--version",
action="version",
version="%(prog)s {}".format(natsort.__version__),
version=f"%(prog)s {natsort.__version__}",
)
parser.add_argument(
"-p",
Expand Down Expand Up @@ -201,9 +199,9 @@ def range_check(low: Num, high: Num) -> NumPair:

"""
if low >= high:
raise ValueError("low >= high")
else:
return low, high
msg = "low >= high"
raise ValueError(msg)
return low, high


def check_filters(filters: Optional[NumPairIter]) -> Optional[List[NumPair]]:
Expand Down Expand Up @@ -231,7 +229,7 @@ def check_filters(filters: Optional[NumPairIter]) -> Optional[List[NumPair]]:
try:
return [range_check(f[0], f[1]) for f in filters]
except ValueError as err:
raise ValueError("Error in --filter: " + str(err))
raise ValueError("Error in --filter: " + str(err)) from None


def keep_entry_range(
Expand Down Expand Up @@ -273,7 +271,10 @@ def keep_entry_range(


def keep_entry_value(
entry: str, values: NumIter, converter: NumConverter, regex: Pattern[str]
entry: str,
values: NumIter,
converter: NumConverter,
regex: Pattern[str],
) -> bool:
"""
Check if an entry does not match a given value.
Expand Down
2 changes: 1 addition & 1 deletion natsort/compat/fake_fastnumbers.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
"""
This module is intended to replicate some of the functionality
from the fastnumbers module in the event that module is not installed.
"""

import unicodedata
from typing import Callable, FrozenSet, Union

Expand Down
Loading
Loading