Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-blurb for openSUSE:Factory checked in at 2025-01-28 14:59:48 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-blurb (Old) and /work/SRC/openSUSE:Factory/.python-blurb.new.2316 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-blurb" Tue Jan 28 14:59:48 2025 rev:6 rq:1240752 version:2.0.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-blurb/python-blurb.changes 2024-12-11 21:09:27.585798934 +0100 +++ /work/SRC/openSUSE:Factory/.python-blurb.new.2316/python-blurb.changes 2025-01-28 15:00:45.133961355 +0100 @@ -1,0 +2,12 @@ +Mon Jan 27 13:55:01 UTC 2025 - Nico Krapp <[email protected]> + +- Update to 2.0.0 + * Move blurb test subcommand into test suite by @hugovk in #37 + * Add support for Python 3.14 by @ezio-melotti in #40 + * Validate gh-issue is int before checking range, and that gh-issue or bpo exists by @hugovk in #35 + * Replace safe_mkdir(path) with os.makedirs(path, exist_ok=True) by @hugovk in #38 + * Test version handling functions by @hugovk in #36 + * Test textwrap_body, current_date and sortable_datetime by @hugovk in #42 + * CI: Lint and test via uv by @hugovk in #32 + +------------------------------------------------------------------- Old: ---- blurb-1.3.0.tar.gz New: ---- blurb-2.0.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-blurb.spec ++++++ --- /var/tmp/diff_new_pack.Jdoxgq/_old 2025-01-28 15:00:45.533977893 +0100 +++ /var/tmp/diff_new_pack.Jdoxgq/_new 2025-01-28 15:00:45.533977893 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-blurb # -# Copyright (c) 2024 SUSE LLC +# Copyright (c) 2025 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,7 +19,7 @@ %define skip_python2 1 %{?sle15_python_module_pythons} Name: python-blurb -Version: 1.3.0 +Version: 2.0.0 Release: 0 Summary: Command-line tool to manage CPython Misc/NEWS.d entries License: BSD-3-Clause @@ -31,6 +31,7 @@ BuildRequires: %{python_module pip} BuildRequires: %{python_module pyfakefs} BuildRequires: %{python_module pytest} +BuildRequires: %{python_module time-machine} BuildRequires: %{python_module wheel} BuildRequires: fdupes BuildRequires: python-rpm-macros ++++++ blurb-1.3.0.tar.gz -> blurb-2.0.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/blurb-1.3.0/.coveragerc new/blurb-2.0.0/.coveragerc --- old/blurb-1.3.0/.coveragerc 2024-10-19 15:56:16.000000000 +0200 +++ new/blurb-2.0.0/.coveragerc 2025-01-15 13:44:34.000000000 +0100 @@ -10,3 +10,4 @@ [run] omit = **/blurb/__main__.py + **/blurb/_version.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/blurb-1.3.0/.github/workflows/lint.yml new/blurb-2.0.0/.github/workflows/lint.yml --- old/blurb-1.3.0/.github/workflows/lint.yml 2024-10-19 15:56:16.000000000 +0200 +++ new/blurb-2.0.0/.github/workflows/lint.yml 2025-01-15 13:44:34.000000000 +0100 @@ -14,8 +14,9 @@ steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-python@v5 with: python-version: "3.x" - cache: pip - - uses: pre-commit/[email protected] + - uses: tox-dev/action-pre-commit-uv@v1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/blurb-1.3.0/.github/workflows/release.yml new/blurb-2.0.0/.github/workflows/release.yml --- old/blurb-1.3.0/.github/workflows/release.yml 2024-10-19 15:56:16.000000000 +0200 +++ new/blurb-2.0.0/.github/workflows/release.yml 2025-01-15 13:44:34.000000000 +0100 @@ -24,6 +24,7 @@ - uses: actions/checkout@v4 with: fetch-depth: 0 + persist-credentials: false - uses: hynek/build-and-inspect-python-package@v2 @@ -50,7 +51,6 @@ - name: Publish to Test PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: - attestations: true repository-url: https://test.pypi.org/legacy/ # Publish to PyPI on GitHub Releases. @@ -82,5 +82,3 @@ - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@release/v1 - with: - attestations: true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/blurb-1.3.0/.github/workflows/test.yml new/blurb-2.0.0/.github/workflows/test.yml --- old/blurb-1.3.0/.github/workflows/test.yml 2024-10-19 15:56:16.000000000 +0200 +++ new/blurb-2.0.0/.github/workflows/test.yml 2025-01-15 13:44:34.000000000 +0100 @@ -14,30 +14,28 @@ strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} allow-prereleases: true - cache: pip - - name: Install dependencies - run: | - python --version - python -m pip install -U pip - python -m pip install -U tox + - name: Install uv + uses: hynek/setup-cached-uv@v2 - name: Tox tests run: | - tox -e py + uvx --with tox-uv tox -e py - name: Upload coverage - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: flags: ${{ matrix.python-version }} name: Python ${{ matrix.python-version }} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/blurb-1.3.0/CHANGELOG.md new/blurb-2.0.0/CHANGELOG.md --- old/blurb-1.3.0/CHANGELOG.md 2024-10-19 15:56:16.000000000 +0200 +++ new/blurb-2.0.0/CHANGELOG.md 2025-01-15 13:44:34.000000000 +0100 @@ -1,5 +1,14 @@ # Changelog +## 2.0.0 + +* Move 'blurb test' subcommand into test suite by @hugovk in https://github.com/python/blurb/pull/37 +* Add support for Python 3.14 by @ezio-melotti in https://github.com/python/blurb/pull/40 +* Validate gh-issue is int before checking range, and that gh-issue or bpo exists by @hugovk in https://github.com/python/blurb/pull/35 +* Replace `safe_mkdir(path)` with `os.makedirs(path, exist_ok=True)` by @hugovk in https://github.com/python/blurb/pull/38 +* Test version handling functions by @hugovk in https://github.com/python/blurb/pull/36 +* CI: Lint and test via uv by @hugovk in https://github.com/python/blurb/pull/32 + ## 1.3.0 * Add support for Python 3.13 by @hugovk in https://github.com/python/blurb/pull/26 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/blurb-1.3.0/PKG-INFO new/blurb-2.0.0/PKG-INFO --- old/blurb-1.3.0/PKG-INFO 2024-10-19 15:56:16.000000000 +0200 +++ new/blurb-2.0.0/PKG-INFO 2025-01-15 13:44:34.000000000 +0100 @@ -1,6 +1,6 @@ -Metadata-Version: 2.3 +Metadata-Version: 2.4 Name: blurb -Version: 1.3.0 +Version: 2.0.0 Summary: Command-line tool to manage CPython Misc/NEWS.d entries. Project-URL: Changelog, https://github.com/python/blurb/blob/main/CHANGELOG.md Project-URL: Homepage, https://github.com/python/blurb @@ -16,11 +16,13 @@ Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 Requires-Python: >=3.9 Provides-Extra: tests Requires-Dist: pyfakefs; extra == 'tests' Requires-Dist: pytest; extra == 'tests' Requires-Dist: pytest-cov; extra == 'tests' +Requires-Dist: time-machine; extra == 'tests' Description-Content-Type: text/markdown # blurb diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/blurb-1.3.0/pyproject.toml new/blurb-2.0.0/pyproject.toml --- old/blurb-1.3.0/pyproject.toml 2024-10-19 15:56:16.000000000 +0200 +++ new/blurb-2.0.0/pyproject.toml 2025-01-15 13:44:34.000000000 +0100 @@ -25,6 +25,7 @@ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", ] dynamic = [ "version", @@ -33,6 +34,7 @@ "pyfakefs", "pytest", "pytest-cov", + "time-machine", ] urls.Changelog = "https://github.com/python/blurb/blob/main/CHANGELOG.md" urls.Homepage = "https://github.com/python/blurb" @@ -49,4 +51,4 @@ local_scheme = "no-local-version" [tool.pyproject-fmt] -max_supported_python = "3.13" +max_supported_python = "3.14" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/blurb-1.3.0/src/blurb/_version.py new/blurb-2.0.0/src/blurb/_version.py --- old/blurb-1.3.0/src/blurb/_version.py 2024-10-19 15:56:16.000000000 +0200 +++ new/blurb-2.0.0/src/blurb/_version.py 2025-01-15 13:44:34.000000000 +0100 @@ -12,5 +12,5 @@ __version_tuple__: VERSION_TUPLE version_tuple: VERSION_TUPLE -__version__ = version = '1.3.0' -__version_tuple__ = version_tuple = (1, 3, 0) +__version__ = version = '2.0.0' +__version_tuple__ = version_tuple = (2, 0, 0) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/blurb-1.3.0/src/blurb/blurb.py new/blurb-2.0.0/src/blurb/blurb.py --- old/blurb-1.3.0/src/blurb/blurb.py 2024-10-19 15:56:16.000000000 +0200 +++ new/blurb-2.0.0/src/blurb/blurb.py 2025-01-15 13:44:34.000000000 +0100 @@ -57,7 +57,6 @@ import tempfile import textwrap import time -import unittest from . import __version__ @@ -140,7 +139,6 @@ return _unsanitize_section.get(section, section) def next_filename_unsanitize_sections(filename): - s = filename for key, value in _unsanitize_section.items(): for separator in "/\\": key = f"{separator}{key}{separator}" @@ -268,11 +266,6 @@ os.chdir(self.previous_cwd) -def safe_mkdir(path): - if not os.path.exists(path): - os.makedirs(path) - - def version_key(element): fields = list(element.split(".")) if len(fields) == 1: @@ -482,14 +475,14 @@ # we'll complain about the *first* error # we see in the blurb file, which is a # better user experience. - if key == "gh-issue" and int(value) < lowest_possible_gh_issue_number: - throw(f"The gh-issue number must be {lowest_possible_gh_issue_number} or above, not a PR number.") - if key in issue_keys: try: int(value) except (TypeError, ValueError): - throw(f"Invalid {issue_keys[key]} issue number! ({value!r})") + throw(f"Invalid {issue_keys[key]} number: {value!r}") + + if key == "gh-issue" and int(value) < lowest_possible_gh_issue_number: + throw(f"Invalid gh-issue number: {value!r} (must be >= {lowest_possible_gh_issue_number})") if key == "section": if no_changes: @@ -497,7 +490,10 @@ if value not in sections: throw(f"Invalid section {value!r}! You must use one of the predefined sections.") - if not 'section' in metadata: + if "gh-issue" not in metadata and "bpo" not in metadata: + throw("'gh-issue:' or 'bpo:' must be specified in the metadata!") + + if 'section' not in metadata: throw("No 'section' specified. You must provide one!") self.append((metadata, text)) @@ -557,7 +553,7 @@ def save(self, path): dirname = os.path.dirname(path) - safe_mkdir(dirname) + os.makedirs(dirname, exist_ok=True) text = str(self) with open(path, "wt", encoding="utf-8") as file: @@ -639,42 +635,6 @@ return filename -tests_run = 0 - -class TestParserPasses(unittest.TestCase): - directory = "tests/pass" - - def filename_test(self, filename): - b = Blurbs() - b.load(filename) - self.assertTrue(b) - if os.path.exists(filename + '.res'): - with open(filename + '.res', encoding='utf-8') as file: - expected = file.read() - self.assertEqual(str(b), expected) - - def test_files(self): - global tests_run - with pushd(self.directory): - for filename in glob.glob("*"): - if filename[-4:] == '.res': - self.assertTrue(os.path.exists(filename[:-4]), filename) - continue - self.filename_test(filename) - print(".", end="") - sys.stdout.flush() - tests_run += 1 - - -class TestParserFailures(TestParserPasses): - directory = "tests/fail" - - def filename_test(self, filename): - b = Blurbs() - with self.assertRaises(Exception): - b.load(filename) - - readme_re = re.compile(r"This is \w+ version \d+\.\d+").match def chdir_to_repo_root(): @@ -838,36 +798,6 @@ return None -@subcommand -def test(*args): - """ -Run unit tests. Only works inside source repo, not when installed. - """ - # unittest.main doesn't work because this isn't a module - # so we'll do it ourselves - - while (blurb_dir := _find_blurb_dir()) is None: - old_dir = os.getcwd() - os.chdir("..") - if old_dir == os.getcwd(): - # we reached the root and never found it! - sys.exit("Error: Couldn't find the root of your blurb repo!") - os.chdir(blurb_dir) - - print("-" * 79) - - for clsname, cls in sorted(globals().items()): - if clsname.startswith("Test") and isinstance(cls, type): - o = cls() - for fnname in sorted(dir(o)): - if fnname.startswith("test"): - fn = getattr(o, fnname) - if callable(fn): - fn() - print() - print(tests_run, "tests passed.") - - def find_editor(): for var in 'GIT_EDITOR', 'EDITOR': editor = os.environ.get(var) @@ -1175,12 +1105,12 @@ Creates and populates the Misc/NEWS.d directory tree. """ os.chdir("Misc") - safe_mkdir("NEWS.d/next") + os.makedirs("NEWS.d/next", exist_ok=True) for section in sections: dir_name = sanitize_section(section) dir_path = f"NEWS.d/next/{dir_name}" - safe_mkdir(dir_path) + os.makedirs(dir_path, exist_ok=True) readme_path = f"NEWS.d/next/{dir_name}/README.rst" with open(readme_path, "wt", encoding="utf-8") as readme: readme.write(f"Put news entry ``blurb`` files for the *{section}* section in this directory.\n") @@ -1224,7 +1154,7 @@ fn = get_subcommand(subcommand) # hack - if fn in (help, test, version): + if fn in (help, version): sys.exit(fn(*args)) try: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/blurb-1.3.0/tests/test_blurb.py new/blurb-2.0.0/tests/test_blurb.py --- old/blurb-1.3.0/tests/test_blurb.py 2024-10-19 15:56:16.000000000 +0200 +++ new/blurb-2.0.0/tests/test_blurb.py 2025-01-15 13:44:34.000000000 +0100 @@ -1,5 +1,5 @@ import pytest -from pyfakefs.fake_filesystem import FakeFilesystem +import time_machine from blurb import blurb @@ -45,6 +45,116 @@ assert unsanitized == expected [email protected]( + "body, subsequent_indent, expected", + ( + ( + "This is a test of the textwrap_body function with a string. It should wrap the text to 79 characters.", + "", + "This is a test of the textwrap_body function with a string. It should wrap\n" + "the text to 79 characters.\n", + ), + ( + [ + "This is a test of the textwrap_body function", + "with an iterable of strings.", + "It should wrap the text to 79 characters.", + ], + "", + "This is a test of the textwrap_body function with an iterable of strings. It\n" + "should wrap the text to 79 characters.\n", + ), + ( + "This is a test of the textwrap_body function with a string and subsequent indent.", + " ", + "This is a test of the textwrap_body function with a string and subsequent\n" + " indent.\n", + ), + ( + "This is a test of the textwrap_body function with a bullet list and subsequent indent. The list should not be wrapped.\n" + "\n" + "* Item 1\n" + "* Item 2\n", + " ", + "This is a test of the textwrap_body function with a bullet list and\n" + " subsequent indent. The list should not be wrapped.\n" + "\n" + " * Item 1\n" + " * Item 2\n", + ), + ), +) +def test_textwrap_body(body, subsequent_indent, expected): + assert blurb.textwrap_body(body, subsequent_indent=subsequent_indent) == expected + + +@time_machine.travel("2025-01-07") +def test_current_date(): + assert blurb.current_date() == "2025-01-07" + + +@time_machine.travel("2025-01-07 16:28:41") +def test_sortable_datetime(): + assert blurb.sortable_datetime() == "2025-01-07-16-28-41" + + [email protected]( + "version1, version2", + ( + ("2", "3"), + ("3.5.0a1", "3.5.0b1"), + ("3.5.0a1", "3.5.0rc1"), + ("3.5.0a1", "3.5.0"), + ("3.6.0b1", "3.6.0b2"), + ("3.6.0b1", "3.6.0rc1"), + ("3.6.0b1", "3.6.0"), + ("3.7.0rc1", "3.7.0rc2"), + ("3.7.0rc1", "3.7.0"), + ("3.8", "3.8.1"), + ), +) +def test_version_key(version1, version2): + # Act + key1 = blurb.version_key(version1) + key2 = blurb.version_key(version2) + + # Assert + assert key1 < key2 + + +def test_glob_versions(fs): + # Arrange + fake_version_blurbs = ( + "Misc/NEWS.d/3.7.0.rst", + "Misc/NEWS.d/3.7.0a1.rst", + "Misc/NEWS.d/3.7.0a2.rst", + "Misc/NEWS.d/3.7.0b1.rst", + "Misc/NEWS.d/3.7.0b2.rst", + "Misc/NEWS.d/3.7.0rc1.rst", + "Misc/NEWS.d/3.7.0rc2.rst", + "Misc/NEWS.d/3.9.0b1.rst", + "Misc/NEWS.d/3.12.0a1.rst", + ) + for fn in fake_version_blurbs: + fs.create_file(fn) + + # Act + versions = blurb.glob_versions() + + # Assert + assert versions == [ + "3.12.0a1", + "3.9.0b1", + "3.7.0", + "3.7.0rc2", + "3.7.0rc1", + "3.7.0b2", + "3.7.0b1", + "3.7.0a2", + "3.7.0a1", + ] + + def test_glob_blurbs_next(fs): # Arrange fake_news_entries = ( @@ -105,6 +215,22 @@ @pytest.mark.parametrize( + "version, expected", + ( + ("next", "next"), + ("3.12.0a1", "3.12.0 alpha 1"), + ("3.12.0b2", "3.12.0 beta 2"), + ("3.12.0rc2", "3.12.0 release candidate 2"), + ("3.12.0", "3.12.0 final"), + ("3.12.1", "3.12.1 final"), + ), +) +def test_printable_version(version, expected): + # Act / Assert + assert blurb.printable_version(version) == expected + + [email protected]( "news_entry, expected_section", ( ( @@ -188,3 +314,68 @@ # Assert captured = capfd.readouterr() assert captured.out.startswith("blurb version ") + + +def test_parse(): + # Arrange + contents = ".. gh-issue: 123456\n.. section: IDLE\nHello world!" + blurbs = blurb.Blurbs() + + # Act + blurbs.parse(contents) + + # Assert + metadata, body = blurbs[0] + assert metadata["gh-issue"] == "123456" + assert metadata["section"] == "IDLE" + assert body == "Hello world!\n" + + [email protected]( + "contents, expected_error", + ( + ( + "", + r"Blurb 'body' text must not be empty!", + ), + ( + "gh-issue: Hello world!", + r"Blurb 'body' can't start with 'gh-'!", + ), + ( + ".. gh-issue: 1\n.. section: IDLE\nHello world!", + r"Invalid gh-issue number: '1' \(must be >= 32426\)", + ), + ( + ".. bpo: one-two\n.. section: IDLE\nHello world!", + r"Invalid bpo number: 'one-two'", + ), + ( + ".. gh-issue: one-two\n.. section: IDLE\nHello world!", + r"Invalid GitHub number: 'one-two'", + ), + ( + ".. gh-issue: 123456\n.. section: Funky Kong\nHello world!", + r"Invalid section 'Funky Kong'! You must use one of the predefined sections", + ), + ( + ".. gh-issue: 123456\nHello world!", + r"No 'section' specified. You must provide one!", + ), + ( + ".. gh-issue: 123456\n.. section: IDLE\n.. section: IDLE\nHello world!", + r"Blurb metadata sets 'section' twice!", + ), + ( + ".. section: IDLE\nHello world!", + r"'gh-issue:' or 'bpo:' must be specified in the metadata!", + ), + ), +) +def test_parse_no_body(contents, expected_error): + # Arrange + blurbs = blurb.Blurbs() + + # Act / Assert + with pytest.raises(blurb.BlurbError, match=expected_error): + blurbs.parse(contents) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/blurb-1.3.0/tests/test_parser.py new/blurb-2.0.0/tests/test_parser.py --- old/blurb-1.3.0/tests/test_parser.py 1970-01-01 01:00:00.000000000 +0100 +++ new/blurb-2.0.0/tests/test_parser.py 2025-01-15 13:44:34.000000000 +0100 @@ -0,0 +1,36 @@ +import glob +import os + +import pytest + +from blurb.blurb import Blurbs, pushd + + +class TestParserPasses: + directory = "tests/pass" + + def filename_test(self, filename): + b = Blurbs() + b.load(filename) + assert b + if os.path.exists(filename + ".res"): + with open(filename + ".res", encoding="utf-8") as file: + expected = file.read() + assert str(b) == expected + + def test_files(self): + with pushd(self.directory): + for filename in glob.glob("*"): + if filename.endswith(".res"): + assert os.path.exists(filename[:-4]), filename + continue + self.filename_test(filename) + + +class TestParserFailures(TestParserPasses): + directory = "tests/fail" + + def filename_test(self, filename): + b = Blurbs() + with pytest.raises(Exception): + b.load(filename) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/blurb-1.3.0/tox.ini new/blurb-2.0.0/tox.ini --- old/blurb-1.3.0/tox.ini 2024-10-19 15:56:16.000000000 +0200 +++ new/blurb-2.0.0/tox.ini 2025-01-15 13:44:34.000000000 +0100 @@ -2,7 +2,7 @@ requires = tox>=4.2 env_list = - py{313, 312, 311, 310, 39} + py{314, 313, 312, 311, 310, 39} [testenv] extras = @@ -17,9 +17,7 @@ --cov-report term \ --cov-report xml \ {posargs} - blurb test blurb help blurb --version - {envpython} -I -m blurb test {envpython} -I -m blurb help {envpython} -I -m blurb version
