Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-jaraco.text for openSUSE:Factory checked in at 2026-03-17 19:02:05 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-jaraco.text (Old) and /work/SRC/openSUSE:Factory/.python-jaraco.text.new.8177 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-jaraco.text" Tue Mar 17 19:02:05 2026 rev:18 rq:1339134 version:4.2.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-jaraco.text/python-jaraco.text.changes 2025-04-11 16:45:38.804367832 +0200 +++ /work/SRC/openSUSE:Factory/.python-jaraco.text.new.8177/python-jaraco.text.changes 2026-03-17 19:02:12.698998759 +0100 @@ -1,0 +2,14 @@ +Sun Mar 15 15:35:56 UTC 2026 - Dirk Müller <[email protected]> + +- update to 4.2.0: + * Replaced autocommand usage with Typer -- by :user:`Avasam` + * Fixed join_continuation's parameter type annotation -- by + :user:`Avasam` + * Complete annotations and add py.typed marker -- by + :user:`Avasam` + * Add support for start and end in jaraco.text.FoldedCase.index + -- by :user:`Avasam` + * Support None splitter argument in + jaraco.text.FoldedCase.split -- by :user:`Avasam` + +------------------------------------------------------------------- Old: ---- jaraco_text-4.0.0.tar.gz New: ---- jaraco_text-4.2.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-jaraco.text.spec ++++++ --- /var/tmp/diff_new_pack.7lBdzE/_old 2026-03-17 19:02:13.575034683 +0100 +++ /var/tmp/diff_new_pack.7lBdzE/_new 2026-03-17 19:02:13.579034847 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-jaraco.text # -# Copyright (c) 2025 SUSE LLC +# Copyright (c) 2026 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,7 +18,7 @@ %{?sle15_python_module_pythons} Name: python-jaraco.text -Version: 4.0.0 +Version: 4.2.0 Release: 0 Summary: Tools to work with text License: MIT @@ -34,6 +34,7 @@ BuildRequires: %{python_module pytest} BuildRequires: %{python_module setuptools_scm >= 3.4.1} BuildRequires: %{python_module setuptools} +BuildRequires: %{python_module typer} BuildRequires: %{python_module wheel} BuildRequires: fdupes BuildRequires: python-rpm-macros @@ -45,6 +46,7 @@ Requires: python-jaraco.context >= 4.1 Requires: python-jaraco.functools Requires: python-more-itertools +Requires: python-typer BuildArch: noarch %python_subpackages ++++++ jaraco_text-4.0.0.tar.gz -> jaraco_text-4.2.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/.coveragerc new/jaraco_text-4.2.0/.coveragerc --- old/jaraco_text-4.0.0/.coveragerc 2024-07-26 20:08:11.000000000 +0200 +++ new/jaraco_text-4.2.0/.coveragerc 2026-02-10 00:29:39.000000000 +0100 @@ -8,6 +8,8 @@ [report] show_missing = True exclude_also = - # jaraco/skeleton#97 - @overload + # Exclude common false positives per + # https://coverage.readthedocs.io/en/latest/excluding.html#advanced-exclusion + # Ref jaraco/skeleton#97 and jaraco/skeleton#135 + class .*\bProtocol\): if TYPE_CHECKING: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/.github/dependabot.yml new/jaraco_text-4.2.0/.github/dependabot.yml --- old/jaraco_text-4.0.0/.github/dependabot.yml 2024-07-26 20:08:11.000000000 +0200 +++ new/jaraco_text-4.2.0/.github/dependabot.yml 1970-01-01 01:00:00.000000000 +0100 @@ -1,8 +0,0 @@ -version: 2 -updates: - - package-ecosystem: "pip" - directory: "/" - schedule: - interval: "daily" - allow: - - dependency-type: "all" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/.github/workflows/main.yml new/jaraco_text-4.2.0/.github/workflows/main.yml --- old/jaraco_text-4.0.0/.github/workflows/main.yml 2024-07-26 20:08:11.000000000 +0200 +++ new/jaraco_text-4.2.0/.github/workflows/main.yml 2026-02-10 00:29:39.000000000 +0100 @@ -10,6 +10,7 @@ # required if branches-ignore is supplied (jaraco/skeleton#103) - '**' pull_request: + workflow_dispatch: permissions: contents: read @@ -20,7 +21,6 @@ # Suppress noisy pip warnings PIP_DISABLE_PIP_VERSION_CHECK: 'true' - PIP_NO_PYTHON_VERSION_WARNING: 'true' PIP_NO_WARN_SCRIPT_LOCATION: 'true' # Ensure tests can sense settings about the environment @@ -34,27 +34,36 @@ # https://blog.jaraco.com/efficient-use-of-ci-resources/ matrix: python: - - "3.8" - - "3.12" + - "3.9" + - "3.13" platform: - ubuntu-latest - macos-latest - windows-latest include: - - python: "3.9" - platform: ubuntu-latest - python: "3.10" platform: ubuntu-latest - python: "3.11" platform: ubuntu-latest + - python: "3.12" + platform: ubuntu-latest + - python: "3.14" + platform: ubuntu-latest - python: pypy3.10 platform: ubuntu-latest runs-on: ${{ matrix.platform }} - continue-on-error: ${{ matrix.python == '3.13' }} + continue-on-error: ${{ matrix.python == '3.14' }} steps: - uses: actions/checkout@v4 + - name: Install build dependencies + # Install dependencies for building packages on pre-release Pythons + # jaraco/skeleton#161 + if: matrix.python == '3.14' && matrix.platform == 'ubuntu-latest' + run: | + sudo apt update + sudo apt install -y libxml2-dev libxslt-dev - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} allow-prereleases: true @@ -76,7 +85,7 @@ with: fetch-depth: 0 - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.x - name: Install tox @@ -110,7 +119,7 @@ steps: - uses: actions/checkout@v4 - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.x - name: Install tox diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/.pre-commit-config.yaml new/jaraco_text-4.2.0/.pre-commit-config.yaml --- old/jaraco_text-4.0.0/.pre-commit-config.yaml 2024-07-26 20:08:11.000000000 +0200 +++ new/jaraco_text-4.2.0/.pre-commit-config.yaml 2026-02-10 00:29:39.000000000 +0100 @@ -1,6 +1,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.8 + rev: v0.12.0 hooks: - id: ruff + args: [--fix, --unsafe-fixes] - id: ruff-format diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/.readthedocs.yaml new/jaraco_text-4.2.0/.readthedocs.yaml --- old/jaraco_text-4.0.0/.readthedocs.yaml 2024-07-26 20:08:11.000000000 +0200 +++ new/jaraco_text-4.2.0/.readthedocs.yaml 2026-02-10 00:29:39.000000000 +0100 @@ -5,6 +5,9 @@ extra_requirements: - doc +sphinx: + configuration: docs/conf.py + # required boilerplate readthedocs/readthedocs.org#10401 build: os: ubuntu-lts-latest diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/LICENSE new/jaraco_text-4.2.0/LICENSE --- old/jaraco_text-4.0.0/LICENSE 2024-07-26 20:08:11.000000000 +0200 +++ new/jaraco_text-4.2.0/LICENSE 2026-02-10 00:30:01.000000000 +0100 @@ -1,17 +1,18 @@ -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to -deal in the Software without restriction, including without limitation the -rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -sell copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +MIT License -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +Copyright (c) 2026 <copyright holders> -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -IN THE SOFTWARE. +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO +EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/NEWS.rst new/jaraco_text-4.2.0/NEWS.rst --- old/jaraco_text-4.0.0/NEWS.rst 2024-07-26 20:08:11.000000000 +0200 +++ new/jaraco_text-4.2.0/NEWS.rst 2026-02-10 00:29:39.000000000 +0100 @@ -1,3 +1,29 @@ +v4.2.0 +====== + +Features +-------- + +- Replaced ``autocommand`` usage with ``Typer`` -- by :user:`Avasam` (#25) + + +Bugfixes +-------- + +- Fixed `join_continuation`'s parameter type annotation -- by :user:`Avasam` (#28) + + +v4.1.0 +====== + +Features +-------- + +- Complete annotations and add ``py.typed`` marker -- by :user:`Avasam` (#17) +- Add support for ``start`` and ``end`` in `jaraco.text.FoldedCase.index` -- by :user:`Avasam` (#17) +- Support `None` ``splitter`` argument in `jaraco.text.FoldedCase.split` -- by :user:`Avasam` (#17) + + v4.0.0 ====== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/PKG-INFO new/jaraco_text-4.2.0/PKG-INFO --- old/jaraco_text-4.0.0/PKG-INFO 2024-07-26 20:08:35.644393700 +0200 +++ new/jaraco_text-4.2.0/PKG-INFO 2026-02-10 00:30:02.087462400 +0100 @@ -1,29 +1,23 @@ -Metadata-Version: 2.1 +Metadata-Version: 2.4 Name: jaraco.text -Version: 4.0.0 +Version: 4.2.0 Summary: Module for text manipulation Author-email: "Jason R. Coombs" <[email protected]> +License-Expression: MIT Project-URL: Source, https://github.com/jaraco/jaraco.text Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3 :: Only -Requires-Python: >=3.8 +Requires-Python: >=3.9 Description-Content-Type: text/x-rst License-File: LICENSE -Requires-Dist: jaraco.functools Requires-Dist: jaraco.context>=4.1 -Requires-Dist: importlib_resources; python_version < "3.9" -Requires-Dist: autocommand +Requires-Dist: jaraco.functools Requires-Dist: more_itertools +Requires-Dist: typer-slim Provides-Extra: test Requires-Dist: pytest!=8.1.*,>=6; extra == "test" -Requires-Dist: pytest-checkdocs>=2.4; extra == "test" -Requires-Dist: pytest-cov; extra == "test" -Requires-Dist: pytest-mypy; extra == "test" -Requires-Dist: pytest-enabler>=2.2; extra == "test" -Requires-Dist: pytest-ruff>=0.2.1; sys_platform != "cygwin" and extra == "test" Requires-Dist: pathlib2; python_version < "3.10" and extra == "test" Provides-Extra: doc Requires-Dist: sphinx>=3.5; extra == "doc" @@ -34,6 +28,17 @@ Requires-Dist: jaraco.tidelift>=1.4; extra == "doc" Provides-Extra: inflect Requires-Dist: inflect; extra == "inflect" +Provides-Extra: check +Requires-Dist: pytest-checkdocs>=2.4; extra == "check" +Requires-Dist: pytest-ruff>=0.2.1; sys_platform != "cygwin" and extra == "check" +Provides-Extra: cover +Requires-Dist: pytest-cov; extra == "cover" +Provides-Extra: enabler +Requires-Dist: pytest-enabler>=3.4; extra == "enabler" +Provides-Extra: type +Requires-Dist: pytest-mypy>=1.0.1; extra == "type" +Requires-Dist: mypy<1.19; platform_python_implementation == "PyPy" and extra == "type" +Dynamic: license-file .. image:: https://img.shields.io/pypi/v/jaraco.text.svg :target: https://pypi.org/project/jaraco.text @@ -44,14 +49,14 @@ :target: https://github.com/jaraco/jaraco.text/actions?query=workflow%3A%22tests%22 :alt: tests -.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json +.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json :target: https://github.com/astral-sh/ruff :alt: Ruff .. image:: https://readthedocs.org/projects/jaracotext/badge/?version=latest :target: https://jaracotext.readthedocs.io/en/latest/?badge=latest -.. image:: https://img.shields.io/badge/skeleton-2024-informational +.. image:: https://img.shields.io/badge/skeleton-2025-informational :target: https://blog.jaraco.com/skeleton .. image:: https://tidelift.com/badges/package/pypi/jaraco.text diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/README.rst new/jaraco_text-4.2.0/README.rst --- old/jaraco_text-4.0.0/README.rst 2024-07-26 20:08:11.000000000 +0200 +++ new/jaraco_text-4.2.0/README.rst 2026-02-10 00:29:39.000000000 +0100 @@ -7,14 +7,14 @@ :target: https://github.com/jaraco/jaraco.text/actions?query=workflow%3A%22tests%22 :alt: tests -.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json +.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json :target: https://github.com/astral-sh/ruff :alt: Ruff .. image:: https://readthedocs.org/projects/jaracotext/badge/?version=latest :target: https://jaracotext.readthedocs.io/en/latest/?badge=latest -.. image:: https://img.shields.io/badge/skeleton-2024-informational +.. image:: https://img.shields.io/badge/skeleton-2025-informational :target: https://blog.jaraco.com/skeleton .. image:: https://tidelift.com/badges/package/pypi/jaraco.text diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/conftest.py new/jaraco_text-4.2.0/conftest.py --- old/jaraco_text-4.0.0/conftest.py 2024-07-26 20:08:11.000000000 +0200 +++ new/jaraco_text-4.2.0/conftest.py 2026-02-10 00:29:39.000000000 +0100 @@ -1,3 +1,4 @@ +import pathlib as std_pathlib import sys import pytest @@ -5,11 +6,11 @@ if sys.version_info < (3, 10): import pathlib2 as pathlib # pragma: nocover else: - import pathlib # pragma: nocover + pathlib = std_pathlib # pragma: nocover @pytest.fixture -def tmp_path(tmp_path): +def tmp_path(tmp_path: std_pathlib.Path) -> pathlib.Path: """ Override tmp_path to wrap in a more modern interface. """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/docs/conf.py new/jaraco_text-4.2.0/docs/conf.py --- old/jaraco_text-4.0.0/docs/conf.py 2024-07-26 20:08:11.000000000 +0200 +++ new/jaraco_text-4.2.0/docs/conf.py 2026-02-10 00:29:39.000000000 +0100 @@ -1,3 +1,5 @@ +from __future__ import annotations + extensions = [ 'sphinx.ext.autodoc', 'jaraco.packaging.sphinx', @@ -30,6 +32,7 @@ # Be strict about any broken references nitpicky = True +nitpick_ignore: list[tuple[str, str]] = [] # Include Python intersphinx mapping to prevent failures # jaraco/skeleton#51 @@ -41,4 +44,26 @@ # Preserve authored syntax for defaults autodoc_preserve_defaults = True +# Add support for linking usernames, PyPI projects, Wikipedia pages +github_url = 'https://github.com/' +extlinks = { + 'user': (f'{github_url}%s', '@%s'), + 'pypi': ('https://pypi.org/project/%s', '%s'), + 'wiki': ('https://wikipedia.org/wiki/%s', '%s'), +} +extensions += ['sphinx.ext.extlinks'] + +# local + extensions += ['jaraco.tidelift'] +nitpick_ignore += [ + ("py:class", "FileDescriptorOrPath"), + ("py:class", "_SupportsDecode"), + ("py:class", "_TranslateTable"), + ("py:class", "SupportsGetItem"), + ("py:class", "SupportsNext"), + ("py:class", "SupportsIter"), + ("py:class", "itertools.chain"), + ("py:class", "jaraco.text._SupportsDecode"), + ("py:class", "jaraco.text._T"), +] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/jaraco/text/__init__.py new/jaraco_text-4.2.0/jaraco/text/__init__.py --- old/jaraco_text-4.0.0/jaraco/text/__init__.py 2024-07-26 20:08:11.000000000 +0200 +++ new/jaraco_text-4.2.0/jaraco/text/__init__.py 2026-02-10 00:29:39.000000000 +0100 @@ -1,27 +1,60 @@ +from __future__ import annotations + import functools +import io import itertools +import os import re +import sys import textwrap - -from typing import Iterable - -try: - from importlib.resources import files # type: ignore -except ImportError: # pragma: nocover - from importlib_resources import files # type: ignore +from collections.abc import Callable, Generator, Iterable, Sequence +from importlib.resources import files +from typing import ( + TYPE_CHECKING, + Literal, + Protocol, + SupportsIndex, + TypeVar, + Union, + cast, + overload, +) from jaraco.context import ExceptionTrap from jaraco.functools import compose, method_cache +if sys.version_info >= (3, 11): # pragma: no cover + from importlib.resources.abc import Traversable +else: # pragma: no cover + from importlib.abc import Traversable + +if TYPE_CHECKING: + from _typeshed import ( + FileDescriptorOrPath, + SupportsIter, + SupportsNext, + ) + from typing_extensions import Self, TypeAlias, TypeGuard, Unpack + + Openable: TypeAlias = FileDescriptorOrPath +else: + Openable = Union[str, bytes, os.PathLike, int] + +_T = TypeVar("_T") -def substitution(old, new): + +class _SupportsDecode(Protocol): + def decode(self) -> object: ... + + +def substitution(old: str, new: str) -> Callable[[str], str]: """ Return a function that will perform a substitution on a string """ return lambda s: s.replace(old, new) -def multi_substitution(*substitutions): +def multi_substitution(*substitutions: str) -> Callable[[str], str]: """ Take a sequence of pairs specifying substitutions, and create a function that performs those substitutions. @@ -29,11 +62,13 @@ >>> multi_substitution(('foo', 'bar'), ('bar', 'baz'))('foo') 'baz' """ - substitutions = itertools.starmap(substitution, substitutions) + callables: Iterable[Callable[[str], str]] = itertools.starmap( + substitution, substitutions + ) # compose function applies last function first, so reverse the # substitutions to get the expected order. - substitutions = reversed(tuple(substitutions)) - return compose(*substitutions) + reversed_ = reversed(tuple(callables)) + return compose(*reversed_) class FoldedCase(str): @@ -58,6 +93,11 @@ >>> s.split('O') ['hell', ' w', 'rld'] + Like ``str``, split accepts None as ''. + + >>> s.split(None) + ['hello', 'world'] + >>> sorted(map(FoldedCase, ['GAMMA', 'alpha', 'Beta'])) ['alpha', 'Beta', 'GAMMA'] @@ -97,49 +137,81 @@ >>> FoldedCase('ß') == FoldedCase('ss') True + + Also supports string to object comparisons: + + >>> FoldedCase('foo') == object() + False + >>> FoldedCase('foo') != object() + True + >>> object() in FoldedCase('foo') + False """ - def __lt__(self, other): + def __lt__(self, other: str) -> bool: return self.casefold() < other.casefold() - def __gt__(self, other): + def __gt__(self, other: str) -> bool: return self.casefold() > other.casefold() - def __eq__(self, other): - return self.casefold() == other.casefold() - - def __ne__(self, other): - return self.casefold() != other.casefold() + @functools.singledispatchmethod + def __eq__(self, other: object) -> bool: + return False + + @__eq__.register + def _(self, other: str) -> bool: + return self.casefold().__eq__(other.casefold()) + + @functools.singledispatchmethod + def __ne__(self, other: object) -> bool: + return True + + @__ne__.register + def _(self, other: str) -> bool: + return self.casefold().__ne__(other.casefold()) - def __hash__(self): + def __hash__(self) -> int: return hash(self.casefold()) - def __contains__(self, other): + @functools.singledispatchmethod + def __contains__(self, other: object) -> bool: + return False + + @__contains__.register + def _(self, other: str) -> bool: return super().casefold().__contains__(other.casefold()) - def in_(self, other): - "Does self appear in other?" + def in_(self, other: str) -> bool: + """Does self appear in other?""" return self in FoldedCase(other) # cache casefold since it's likely to be called frequently. @method_cache - def casefold(self): + def casefold(self) -> str: return super().casefold() - def index(self, sub): - return self.casefold().index(sub.casefold()) + def index( + self, + sub: str, + start: SupportsIndex | None = None, + end: SupportsIndex | None = None, + ) -> int: + return self.casefold().index(sub.casefold(), start, end) + + @functools.singledispatchmethod + def split( + self, splitter: str | None = ' ', maxsplit: SupportsIndex = 0 + ) -> list[str]: + return self.split(' ', maxsplit=maxsplit) - def split(self, splitter=' ', maxsplit=0): + @split.register + def _(self, splitter: str, maxsplit: SupportsIndex = 0) -> list[str]: pattern = re.compile(re.escape(splitter), re.I) - return pattern.split(self, maxsplit) - - -# Python 3.8 compatibility -_unicode_trap = ExceptionTrap(UnicodeDecodeError) + return pattern.split(self, int(maxsplit)) -@_unicode_trap.passes -def is_decodable(value): +@ExceptionTrap(UnicodeDecodeError).passes # type: ignore[no-untyped-call, untyped-decorator, unused-ignore, misc] # jaraco/jaraco.context#15 +def is_decodable(value: _SupportsDecode) -> None: r""" Return True if the supplied value is decodable (using the default encoding). @@ -152,7 +224,7 @@ value.decode() -def is_binary(value): +def is_binary(value: _SupportsDecode) -> TypeGuard[bytes]: r""" Return True if the value appears to be binary (that is, it's a byte string and isn't decodable). @@ -165,7 +237,7 @@ return isinstance(value, bytes) and not is_decodable(value) -def trim(s): +def trim(s: str) -> str: r""" Trim something like a docstring to remove the whitespace that is common due to indentation and formatting. @@ -176,7 +248,7 @@ return textwrap.dedent(s).strip() -def wrap(s): +def wrap(s: str) -> str: """ Wrap lines of text, retaining existing newlines as paragraph markers. @@ -209,7 +281,7 @@ return '\n\n'.join(wrapped) -def unwrap(s): +def unwrap(s: str) -> str: r""" Given a multi-line string, return an unwrapped version. @@ -242,14 +314,14 @@ ['hello', ' world', ' this is your', ' master calling'] """ - def __init__(self, *args): + def __init__(self, *args: Unpack[tuple[str | None, SupportsIndex]]) -> None: self.args = args - def __call__(self, s): + def __call__(self, s: str) -> list[str]: return s.split(*self.args) -def indent(string, prefix=' ' * 4): +def indent(string: str, prefix: str = ' ' * 4) -> str: """ >>> indent('foo') ' foo' @@ -257,7 +329,7 @@ return prefix + string -class WordSet(tuple): +class WordSet(tuple[str, ...]): """ Given an identifier, return the words that identifier represents, whether in camel case, underscore-separated, etc. @@ -313,31 +385,31 @@ _pattern = re.compile('([A-Z]?[a-z]+)|([A-Z]+(?![a-z]))') - def capitalized(self): + def capitalized(self) -> WordSet: return WordSet(word.capitalize() for word in self) - def lowered(self): + def lowered(self) -> WordSet: return WordSet(word.lower() for word in self) - def camel_case(self): + def camel_case(self) -> str: return ''.join(self.capitalized()) - def headless_camel_case(self): + def headless_camel_case(self) -> str: words = iter(self) first = next(words).lower() new_words = itertools.chain((first,), WordSet(words).camel_case()) return ''.join(new_words) - def underscore_separated(self): + def underscore_separated(self) -> str: return '_'.join(self) - def dash_separated(self): + def dash_separated(self) -> str: return '-'.join(self) - def space_separated(self): + def space_separated(self) -> str: return ' '.join(self) - def trim_right(self, item): + def trim_right(self, item: str) -> WordSet: """ Remove the item from the end of the set. @@ -350,7 +422,7 @@ """ return self[:-1] if self and self[-1] == item else self - def trim_left(self, item): + def trim_left(self, item: str) -> WordSet: """ Remove the item from the beginning of the set. @@ -363,26 +435,30 @@ """ return self[1:] if self and self[0] == item else self - def trim(self, item): + def trim(self, item: str) -> WordSet: """ >>> WordSet.parse('foo bar').trim('foo') ('bar',) """ return self.trim_left(item).trim_right(item) - def __getitem__(self, item): + @overload # type:ignore[override] # more restricted return type + def __getitem__(self, item: slice) -> WordSet: ... + @overload + def __getitem__(self, item: SupportsIndex) -> str: ... + def __getitem__(self, item: slice | SupportsIndex) -> WordSet | str: result = super().__getitem__(item) - if isinstance(item, slice): - result = WordSet(result) + if isinstance(result, tuple): + return WordSet(result) return result @classmethod - def parse(cls, identifier): + def parse(cls, identifier: str) -> WordSet: matches = cls._pattern.finditer(identifier) return WordSet(match.group(0) for match in matches) @classmethod - def from_class_name(cls, subject): + def from_class_name(cls, subject: object) -> WordSet: return cls.parse(subject.__class__.__name__) @@ -390,7 +466,7 @@ words = WordSet.parse -def simple_html_strip(s): +def simple_html_strip(s: str) -> str: r""" Remove HTML from the string `s`. @@ -428,7 +504,7 @@ separator = ',' - def __iter__(self): + def __iter__(self) -> filter[str]: parts = self.split(self.separator) return filter(None, (part.strip() for part in parts)) @@ -460,24 +536,30 @@ ['abcd\n', '1234\n'] """ - def __init__(self, prefix, lines): + def __init__(self, prefix: str | None, lines: Iterable[str]) -> None: self.prefix = prefix self.lines = map(self, lines) @classmethod - def strip_prefix(cls, lines): + def strip_prefix(cls, lines: Iterable[str]) -> Self: prefix_lines, lines = itertools.tee(lines) prefix = functools.reduce(cls.common_prefix, prefix_lines) return cls(prefix, lines) - def __call__(self, line): + def __call__(self, line: str) -> str: if not self.prefix: return line null, prefix, rest = line.partition(self.prefix) return rest + @overload @staticmethod - def common_prefix(s1, s2): + def common_prefix(s1: str, s2: str) -> str: ... + @overload + @staticmethod + def common_prefix(s1: Sequence[str], s2: Sequence[str]) -> Sequence[str]: ... + @staticmethod + def common_prefix(s1: Sequence[str], s2: Sequence[str]) -> Sequence[str]: """ Return the common prefix of two lines. """ @@ -487,7 +569,7 @@ return s1[:index] -def remove_prefix(text, prefix): +def remove_prefix(text: str, prefix: str) -> str: """ Remove the prefix from the text if it exists. @@ -501,7 +583,7 @@ return rest -def remove_suffix(text, suffix): +def remove_suffix(text: str, suffix: str) -> str: """ Remove the suffix from the text if it exists. @@ -515,7 +597,7 @@ return rest -def normalize_newlines(text): +def normalize_newlines(text: str) -> str: r""" Replace alternate newlines with the canonical newline. @@ -531,12 +613,12 @@ return re.sub(pattern, '\n', text) -def _nonblank(str): +def _nonblank(str: str) -> bool | Literal['']: return str and not str.startswith('#') @functools.singledispatch -def yield_lines(iterable): +def yield_lines(iterable: Iterable[_T] | str) -> itertools.chain[str]: r""" Yield valid lines of a string or iterable. @@ -555,18 +637,18 @@ @yield_lines.register(str) -def _(text): +def _(text: str) -> filter[str]: return clean(text.splitlines()) -def clean(lines: Iterable[str]): +def clean(lines: Iterable[str]) -> filter[str]: """ Yield non-blank, non-comment elements from lines. """ return filter(_nonblank, map(str.strip, lines)) -def drop_comment(line): +def drop_comment(line: str) -> str: """ Drop comments. @@ -581,7 +663,7 @@ return line.partition(' #')[0] -def join_continuation(lines): +def join_continuation(lines: SupportsIter[SupportsNext[str]]) -> Generator[str]: r""" Join lines continued by a trailing backslash. @@ -604,17 +686,25 @@ >>> list(join_continuation(['foo', 'bar\\', 'baz\\'])) ['foo'] """ - lines = iter(lines) - for item in lines: + lines_ = iter(lines) + for item in lines_: # type: ignore[attr-defined] # A bit of a false positive with iteration dunder fallback while item.endswith('\\'): try: - item = item[:-2].strip() + next(lines) + item = item[:-2].strip() + next(lines_) except StopIteration: return yield item -def read_newlines(filename, limit=1024): +# https://docs.python.org/3/library/io.html#io.TextIOBase.newlines +NewlineSpec: TypeAlias = Union[str, tuple[str, ...], None] + + [email protected] +def read_newlines( + filename: Union[Openable, io.TextIOWrapper], # noqa: UP007 # singledispatch uses the annotation at runtime (python 3.9) + limit: int | None = 1024, +) -> NewlineSpec: r""" >>> tmp_path = getfixture('tmp_path') >>> filename = tmp_path / 'out.txt' @@ -628,12 +718,24 @@ >>> read_newlines(filename) ('\r', '\n', '\r\n') """ + if sys.version_info >= (3, 10): + assert isinstance(filename, Openable) + else: # pragma: no cover + filename = cast(Openable, filename) with open(filename, encoding='utf-8') as fp: - fp.read(limit) - return fp.newlines + return read_newlines(fp, limit=limit) + + +@read_newlines.register +def _( + filename: io.TextIOWrapper, + limit: Union[int, None] = 1024, # noqa: UP007 # singledispatch uses the annotation at runtime (python 3.9) +) -> NewlineSpec: + filename.read(limit) + return filename.newlines -def lines_from(input): +def lines_from(input: Traversable) -> Generator[str]: """ Generate lines from a :class:`importlib.resources.abc.Traversable` path. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/jaraco/text/layouts.py new/jaraco_text-4.2.0/jaraco/text/layouts.py --- old/jaraco_text-4.0.0/jaraco/text/layouts.py 2024-07-26 20:08:11.000000000 +0200 +++ new/jaraco_text-4.2.0/jaraco/text/layouts.py 2026-02-10 00:29:39.000000000 +0100 @@ -1,3 +1,14 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Union + +if TYPE_CHECKING: + from _typeshed import SupportsGetItem, SupportsRead + from typing_extensions import TypeAlias + + # Same as builtins._TranslateTable from typeshed + _TranslateTable: TypeAlias = SupportsGetItem[int, Union[str, int, None]] + qwerty = "-=qwertyuiop[]asdfghjkl;'zxcvbnm,./_+QWERTYUIOP{}ASDFGHJKL:\"ZXCVBNM<>?" dvorak = "[]',.pyfgcrl/=aoeuidhtns-;qjkxbmwvz{}\"<>PYFGCRL?+AOEUIDHTNS_:QJKXBMWVZ" @@ -6,7 +17,7 @@ to_qwerty = str.maketrans(dvorak, qwerty) -def translate(input, translation): +def translate(input: str, translation: _TranslateTable) -> str: """ >>> translate('dvorak', to_dvorak) 'ekrpat' @@ -16,7 +27,7 @@ return input.translate(translation) -def _translate_stream(stream, translation): +def _translate_stream(stream: SupportsRead[str], translation: _TranslateTable) -> None: """ >>> import io >>> _translate_stream(io.StringIO('foo'), to_dvorak) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/jaraco/text/show-newlines.py new/jaraco_text-4.2.0/jaraco/text/show-newlines.py --- old/jaraco_text-4.0.0/jaraco/text/show-newlines.py 2024-07-26 20:08:11.000000000 +0200 +++ new/jaraco_text-4.2.0/jaraco/text/show-newlines.py 2026-02-10 00:29:39.000000000 +0100 @@ -1,11 +1,13 @@ -import autocommand +from __future__ import annotations + import inflect +import typer from more_itertools import always_iterable import jaraco.text -def report_newlines(filename): +def report_newlines(input: typer.FileText) -> None: r""" Report the newlines in the indicated file. @@ -19,14 +21,15 @@ >>> report_newlines(filename) newlines are ('\n', '\r\n') """ - newlines = jaraco.text.read_newlines(filename) + newlines = jaraco.text.read_newlines(input) count = len(tuple(always_iterable(newlines))) engine = inflect.engine() print( + # Pyright typing issue: jaraco/inflect#210 engine.plural_noun("newline", count), engine.plural_verb("is", count), repr(newlines), ) -autocommand.autocommand(__name__)(report_newlines) +__name__ == '__main__' and typer.run(report_newlines) # type: ignore[func-returns-value] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/jaraco/text/strip-prefix.py new/jaraco_text-4.2.0/jaraco/text/strip-prefix.py --- old/jaraco_text-4.0.0/jaraco/text/strip-prefix.py 2024-07-26 20:08:11.000000000 +0200 +++ new/jaraco_text-4.2.0/jaraco/text/strip-prefix.py 2026-02-10 00:29:39.000000000 +0100 @@ -1,11 +1,11 @@ import sys -import autocommand +import typer from jaraco.text import Stripper -def strip_prefix(): +def strip_prefix() -> None: r""" Strip any common prefix from stdin. @@ -18,4 +18,4 @@ sys.stdout.writelines(Stripper.strip_prefix(sys.stdin).lines) -autocommand.autocommand(__name__)(strip_prefix) +__name__ == '__main__' and typer.run(strip_prefix) # type: ignore[func-returns-value] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/jaraco/text/to-dvorak.py new/jaraco_text-4.2.0/jaraco/text/to-dvorak.py --- old/jaraco_text-4.0.0/jaraco/text/to-dvorak.py 2024-07-26 20:08:11.000000000 +0200 +++ new/jaraco_text-4.2.0/jaraco/text/to-dvorak.py 2026-02-10 00:29:39.000000000 +0100 @@ -2,4 +2,4 @@ from . import layouts -__name__ == '__main__' and layouts._translate_stream(sys.stdin, layouts.to_dvorak) +__name__ == '__main__' and layouts._translate_stream(sys.stdin, layouts.to_dvorak) # type: ignore[func-returns-value] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/jaraco/text/to-qwerty.py new/jaraco_text-4.2.0/jaraco/text/to-qwerty.py --- old/jaraco_text-4.0.0/jaraco/text/to-qwerty.py 2024-07-26 20:08:11.000000000 +0200 +++ new/jaraco_text-4.2.0/jaraco/text/to-qwerty.py 2026-02-10 00:29:39.000000000 +0100 @@ -2,4 +2,4 @@ from . import layouts -__name__ == '__main__' and layouts._translate_stream(sys.stdin, layouts.to_qwerty) +__name__ == '__main__' and layouts._translate_stream(sys.stdin, layouts.to_qwerty) # type: ignore[func-returns-value] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/jaraco.text.egg-info/PKG-INFO new/jaraco_text-4.2.0/jaraco.text.egg-info/PKG-INFO --- old/jaraco_text-4.0.0/jaraco.text.egg-info/PKG-INFO 2024-07-26 20:08:35.000000000 +0200 +++ new/jaraco_text-4.2.0/jaraco.text.egg-info/PKG-INFO 2026-02-10 00:30:02.000000000 +0100 @@ -1,29 +1,23 @@ -Metadata-Version: 2.1 +Metadata-Version: 2.4 Name: jaraco.text -Version: 4.0.0 +Version: 4.2.0 Summary: Module for text manipulation Author-email: "Jason R. Coombs" <[email protected]> +License-Expression: MIT Project-URL: Source, https://github.com/jaraco/jaraco.text Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3 :: Only -Requires-Python: >=3.8 +Requires-Python: >=3.9 Description-Content-Type: text/x-rst License-File: LICENSE -Requires-Dist: jaraco.functools Requires-Dist: jaraco.context>=4.1 -Requires-Dist: importlib_resources; python_version < "3.9" -Requires-Dist: autocommand +Requires-Dist: jaraco.functools Requires-Dist: more_itertools +Requires-Dist: typer-slim Provides-Extra: test Requires-Dist: pytest!=8.1.*,>=6; extra == "test" -Requires-Dist: pytest-checkdocs>=2.4; extra == "test" -Requires-Dist: pytest-cov; extra == "test" -Requires-Dist: pytest-mypy; extra == "test" -Requires-Dist: pytest-enabler>=2.2; extra == "test" -Requires-Dist: pytest-ruff>=0.2.1; sys_platform != "cygwin" and extra == "test" Requires-Dist: pathlib2; python_version < "3.10" and extra == "test" Provides-Extra: doc Requires-Dist: sphinx>=3.5; extra == "doc" @@ -34,6 +28,17 @@ Requires-Dist: jaraco.tidelift>=1.4; extra == "doc" Provides-Extra: inflect Requires-Dist: inflect; extra == "inflect" +Provides-Extra: check +Requires-Dist: pytest-checkdocs>=2.4; extra == "check" +Requires-Dist: pytest-ruff>=0.2.1; sys_platform != "cygwin" and extra == "check" +Provides-Extra: cover +Requires-Dist: pytest-cov; extra == "cover" +Provides-Extra: enabler +Requires-Dist: pytest-enabler>=3.4; extra == "enabler" +Provides-Extra: type +Requires-Dist: pytest-mypy>=1.0.1; extra == "type" +Requires-Dist: mypy<1.19; platform_python_implementation == "PyPy" and extra == "type" +Dynamic: license-file .. image:: https://img.shields.io/pypi/v/jaraco.text.svg :target: https://pypi.org/project/jaraco.text @@ -44,14 +49,14 @@ :target: https://github.com/jaraco/jaraco.text/actions?query=workflow%3A%22tests%22 :alt: tests -.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json +.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json :target: https://github.com/astral-sh/ruff :alt: Ruff .. image:: https://readthedocs.org/projects/jaracotext/badge/?version=latest :target: https://jaracotext.readthedocs.io/en/latest/?badge=latest -.. image:: https://img.shields.io/badge/skeleton-2024-informational +.. image:: https://img.shields.io/badge/skeleton-2025-informational :target: https://blog.jaraco.com/skeleton .. image:: https://tidelift.com/badges/package/pypi/jaraco.text diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/jaraco.text.egg-info/SOURCES.txt new/jaraco_text-4.2.0/jaraco.text.egg-info/SOURCES.txt --- old/jaraco_text-4.0.0/jaraco.text.egg-info/SOURCES.txt 2024-07-26 20:08:35.000000000 +0200 +++ new/jaraco_text-4.2.0/jaraco.text.egg-info/SOURCES.txt 2026-02-10 00:30:02.000000000 +0100 @@ -14,7 +14,6 @@ towncrier.toml tox.ini .github/FUNDING.yml -.github/dependabot.yml .github/workflows/main.yml docs/conf.py docs/history.rst @@ -27,7 +26,9 @@ jaraco/text/Lorem ipsum.txt jaraco/text/__init__.py jaraco/text/layouts.py +jaraco/text/py.typed jaraco/text/show-newlines.py jaraco/text/strip-prefix.py jaraco/text/to-dvorak.py -jaraco/text/to-qwerty.py \ No newline at end of file +jaraco/text/to-qwerty.py +tests/test_core.py \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/jaraco.text.egg-info/requires.txt new/jaraco_text-4.2.0/jaraco.text.egg-info/requires.txt --- old/jaraco_text-4.0.0/jaraco.text.egg-info/requires.txt 2024-07-26 20:08:35.000000000 +0200 +++ new/jaraco_text-4.2.0/jaraco.text.egg-info/requires.txt 2026-02-10 00:30:02.000000000 +0100 @@ -1,10 +1,16 @@ -jaraco.functools jaraco.context>=4.1 -autocommand +jaraco.functools more_itertools +typer-slim + +[check] +pytest-checkdocs>=2.4 + +[check:sys_platform != "cygwin"] +pytest-ruff>=0.2.1 -[:python_version < "3.9"] -importlib_resources +[cover] +pytest-cov [doc] sphinx>=3.5 @@ -14,18 +20,20 @@ sphinx-lint jaraco.tidelift>=1.4 +[enabler] +pytest-enabler>=3.4 + [inflect] inflect [test] pytest!=8.1.*,>=6 -pytest-checkdocs>=2.4 -pytest-cov -pytest-mypy -pytest-enabler>=2.2 [test:python_version < "3.10"] pathlib2 -[test:sys_platform != "cygwin"] -pytest-ruff>=0.2.1 +[type] +pytest-mypy>=1.0.1 + +[type:platform_python_implementation == "PyPy"] +mypy<1.19 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/mypy.ini new/jaraco_text-4.2.0/mypy.ini --- old/jaraco_text-4.0.0/mypy.ini 2024-07-26 20:08:11.000000000 +0200 +++ new/jaraco_text-4.2.0/mypy.ini 2026-02-10 00:29:39.000000000 +0100 @@ -1,5 +1,24 @@ [mypy] -ignore_missing_imports = True -# required to support namespace packages -# https://github.com/python/mypy/issues/14057 +# Is the project well-typed? +strict = True + +# Early opt-in even when strict = False +warn_unused_ignores = True +warn_redundant_casts = True +enable_error_code = ignore-without-code + +# Support namespace packages per https://github.com/python/mypy/issues/14057 explicit_package_bases = True + +disable_error_code = + # Disable due to many false positives + overload-overlap, + +# Will be replaced by pathlib in 3.10 +[mypy-pathlib2.*] +ignore_missing_imports = True + +# jaraco/jaraco.develop#20 +# Lucretiel/autocommand#38 +[mypy-autocommand.*] +ignore_missing_imports = True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/pyproject.toml new/jaraco_text-4.2.0/pyproject.toml --- old/jaraco_text-4.0.0/pyproject.toml 2024-07-26 20:08:11.000000000 +0200 +++ new/jaraco_text-4.2.0/pyproject.toml 2026-02-10 00:29:39.000000000 +0100 @@ -1,5 +1,10 @@ [build-system] -requires = ["setuptools>=61.2", "setuptools_scm[toml]>=3.4.1"] +requires = [ + "setuptools>=77", + "setuptools_scm[toml]>=3.4.1", + # jaraco/skeleton#174 + "coherent.licensed", +] build-backend = "setuptools.build_meta" [project] @@ -12,17 +17,16 @@ classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", ] -requires-python = ">=3.8" +requires-python = ">=3.9" +license = "MIT" dependencies = [ - "jaraco.functools", "jaraco.context >= 4.1", - 'importlib_resources; python_version < "3.9"', - "autocommand", + "jaraco.functools", "more_itertools", + "typer-slim", ] dynamic = ["version"] @@ -33,15 +37,11 @@ test = [ # upstream "pytest >= 6, != 8.1.*", - "pytest-checkdocs >= 2.4", - "pytest-cov", - "pytest-mypy", - "pytest-enabler >= 2.2", - "pytest-ruff >= 0.2.1; sys_platform != 'cygwin'", # local 'pathlib2; python_version < "3.10"', ] + doc = [ # upstream "sphinx >= 3.5", @@ -57,4 +57,28 @@ ] inflect = ["inflect"] +check = [ + "pytest-checkdocs >= 2.4", + "pytest-ruff >= 0.2.1; sys_platform != 'cygwin'", +] + +cover = [ + "pytest-cov", +] + +enabler = [ + "pytest-enabler >= 3.4", +] + +type = [ + # upstream + "pytest-mypy >= 1.0.1", + + ## workaround for python/mypy#20454 + "mypy < 1.19; python_implementation == 'PyPy'", + + # local +] + + [tool.setuptools_scm] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/ruff.toml new/jaraco_text-4.2.0/ruff.toml --- old/jaraco_text-4.0.0/ruff.toml 2024-07-26 20:08:11.000000000 +0200 +++ new/jaraco_text-4.2.0/ruff.toml 2026-02-10 00:29:39.000000000 +0100 @@ -1,10 +1,31 @@ [lint] extend-select = [ - "C901", - "PERF401", - "W", + # upstream + + "C901", # complex-structure + "I", # isort + "PERF401", # manual-list-comprehension + + # Ensure modern type annotation syntax and best practices + # Not including those covered by type-checkers or exclusive to Python 3.11+ + "FA", # flake8-future-annotations + "F404", # late-future-import + "PYI", # flake8-pyi + "UP006", # non-pep585-annotation + "UP007", # non-pep604-annotation + "UP010", # unnecessary-future-import + "UP035", # deprecated-import + "UP037", # quoted-annotation + "UP043", # unnecessary-default-type-args + + # local ] ignore = [ + # upstream + + # Typeshed rejects complex or non-literal defaults for maintenance and testing reasons, + # irrelevant to this project. + "PYI011", # typed-argument-default-in-stub # https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules "W191", "E111", @@ -18,8 +39,8 @@ "Q003", "COM812", "COM819", - "ISC001", - "ISC002", + + # local ] [format] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/tests/test_core.py new/jaraco_text-4.2.0/tests/test_core.py --- old/jaraco_text-4.0.0/tests/test_core.py 1970-01-01 01:00:00.000000000 +0100 +++ new/jaraco_text-4.2.0/tests/test_core.py 2026-02-10 00:29:39.000000000 +0100 @@ -0,0 +1,30 @@ +from __future__ import annotations + +import pytest + +from jaraco.text import join_continuation + + +def test_join_continuation_typing() -> None: + """ + Ensures that the type annotation for join_continuation matches what we expect at runtime, + since checkers seem to give a false-positive on supporting ``__iter__`` and ``__next__``. + """ + + good: list[str] = ['foo \\', 'bar', 'baz'] + expected: list[str] = ['foobar', 'baz'] + + # Ensure consistency at runtime for different iterables and no type error + assert list(join_continuation(good)) == expected + assert list(join_continuation(tuple(good))) == expected + assert list(join_continuation(map(lambda string: string, good))) == expected + assert list(join_continuation(iter(good))) == expected + assert list(join_continuation({string: None for string in good})) == expected + + # NOTE: str isn't an expected use-case + # but unfortunately a str is an iterable of str, so we can't validate + + # iter supports SupportsGetItem[int, str], but we don't ! + bad: dict[int, str] = {i: string for (i, string) in enumerate(good)} + with pytest.raises(AttributeError): + list(join_continuation(bad)) # type: ignore[arg-type] # Testing for type error here! diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/towncrier.toml new/jaraco_text-4.2.0/towncrier.toml --- old/jaraco_text-4.0.0/towncrier.toml 2024-07-26 20:08:11.000000000 +0200 +++ new/jaraco_text-4.2.0/towncrier.toml 2026-02-10 00:29:39.000000000 +0100 @@ -1,2 +1,3 @@ [tool.towncrier] title_format = "{version}" +directory = "newsfragments" # jaraco/skeleton#184 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jaraco_text-4.0.0/tox.ini new/jaraco_text-4.2.0/tox.ini --- old/jaraco_text-4.0.0/tox.ini 2024-07-26 20:08:11.000000000 +0200 +++ new/jaraco_text-4.2.0/tox.ini 2026-02-10 00:29:39.000000000 +0100 @@ -8,6 +8,10 @@ usedevelop = True extras = test + check + cover + enabler + type inflect [testenv:diffcov] @@ -28,9 +32,7 @@ changedir = docs commands = python -m sphinx -W --keep-going . {toxinidir}/build/html - python -m sphinxlint \ - # workaround for sphinx-contrib/sphinx-lint#83 - --jobs 1 + python -m sphinxlint [testenv:finalize] description = assemble changelog and tag a release
