Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-click-extra for openSUSE:Factory checked in at 2026-03-05 17:15:01 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-click-extra (Old) and /work/SRC/openSUSE:Factory/.python-click-extra.new.561 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-click-extra" Thu Mar 5 17:15:01 2026 rev:16 rq:1336598 version:7.6.4 Changes: -------- --- /work/SRC/openSUSE:Factory/python-click-extra/python-click-extra.changes 2026-03-02 17:42:08.795877323 +0100 +++ /work/SRC/openSUSE:Factory/.python-click-extra.new.561/python-click-extra.changes 2026-03-05 17:18:20.786595304 +0100 @@ -1,0 +2,15 @@ +Wed Mar 4 21:45:16 UTC 2026 - Dirk MΓΌller <[email protected]> + +- update to 7.6.4: + * Fix `ExtraVersionOption.cli_frame()` crashing in Nuitka- + compiled binaries where all stack frames belong to the Click + ecosystem. + * Fix `ExtraVersionOption.module_version` returning `None` in + `__main__` entry points by checking the parent package's + `__version__`. + * Fix test plan for Nuitka-compiled binary. + * Add `@pytest.mark.once` marker for platform-independent + structural tests. Run them in a single CI job instead of + across the full matrix. + +------------------------------------------------------------------- Old: ---- click-extra-7.6.3.tar.gz New: ---- click-extra-7.6.4.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-click-extra.spec ++++++ --- /var/tmp/diff_new_pack.BlBlE5/_old 2026-03-05 17:18:21.362619143 +0100 +++ /var/tmp/diff_new_pack.BlBlE5/_new 2026-03-05 17:18:21.362619143 +0100 @@ -27,7 +27,7 @@ %{?sle15_python_module_pythons} Name: python-click-extra -Version: 7.6.3 +Version: 7.6.4 Release: 0 Summary: Drop-in replacement for Click to make user-friendly and colorful CLI License: GPL-2.0-or-later ++++++ click-extra-7.6.3.tar.gz -> click-extra-7.6.4.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/click-extra-7.6.3/.github/workflows/tests.yaml new/click-extra-7.6.4/.github/workflows/tests.yaml --- old/click-extra-7.6.3/.github/workflows/tests.yaml 2026-03-02 10:52:22.000000000 +0100 +++ new/click-extra-7.6.4/.github/workflows/tests.yaml 2026-03-04 15:50:27.000000000 +0100 @@ -30,6 +30,19 @@ jobs: + once-tests: + name: π Once tests + runs-on: ubuntu-slim + steps: + - uses: actions/[email protected] + - uses: astral-sh/[email protected] + - name: Install Python + run: uv --no-progress venv --python 3.14 + - name: Install project + run: uv --no-progress sync --frozen --all-extras --group test + - name: Run once-only tests + run: uv --no-progress run --frozen -- pytest -m once + tests: strategy: # We delegate the failure decision to each job, by the way of the "continue-on-error" flag. @@ -199,7 +212,7 @@ ${{ matrix.cloup-version != 'released' && format('--with "git+https://github.com/janluke/cloup.git@{0}"', matrix.cloup-version) || ''}} -- - pytest + pytest -m "not once" - name: Codecov - coverage uses: codecov/[email protected] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/click-extra-7.6.3/changelog.md new/click-extra-7.6.4/changelog.md --- old/click-extra-7.6.3/changelog.md 2026-03-02 10:52:22.000000000 +0100 +++ new/click-extra-7.6.4/changelog.md 2026-03-04 15:50:27.000000000 +0100 @@ -1,19 +1,38 @@ # Changelog +## [7.6.4 (2026-03-04)](https://github.com/kdeldycke/click-extra/compare/v7.6.3...v7.6.4) + +- Fix `ExtraVersionOption.cli_frame()` crashing in Nuitka-compiled binaries where all stack frames belong to the Click ecosystem. +- Fix `ExtraVersionOption.module_version` returning `None` in `__main__` entry points by checking the parent package's `__version__`. +- Fix test plan for Nuitka-compiled binary. +- Add `@pytest.mark.once` marker for platform-independent structural tests. Run them in a single CI job instead of across the full matrix. + ## [7.6.3 (2026-03-02)](https://github.com/kdeldycke/click-extra/compare/v7.6.2...v7.6.3) +> [!NOTE] +> `7.6.3` is available on [π PyPI](https://pypi.org/project/click-extra/7.6.3/) and [π GitHub](https://github.com/kdeldycke/click-extra/releases/tag/v7.6.3). + - Fix `test_default_pattern_roaming_force_posix` test failures when `XDG_CONFIG_HOME` is set. Closes {issue}`1541`. ## [7.6.2 (2026-02-27)](https://github.com/kdeldycke/click-extra/compare/v7.6.1...v7.6.2) +> [!NOTE] +> `7.6.2` is available on [π PyPI](https://pypi.org/project/click-extra/7.6.2/) and [π GitHub](https://github.com/kdeldycke/click-extra/releases/tag/v7.6.2). + - Add `ExtraVersionOption.prebake_version()` static method to pre-bake `__version__` strings with Git hashes at compile time, complementing the runtime `version` property for Nuitka/PyInstaller binaries. ## [7.6.1 (2026-02-27)](https://github.com/kdeldycke/click-extra/compare/v7.6.0...v7.6.1) +> [!NOTE] +> `7.6.1` is available on [π PyPI](https://pypi.org/project/click-extra/7.6.1/) and [π GitHub](https://github.com/kdeldycke/click-extra/releases/tag/v7.6.1). + - Fix test failures when optional config format dependencies are not installed. Closes {issue}`1538`. ## [7.6.0 (2026-02-26)](https://github.com/kdeldycke/click-extra/compare/v7.5.3...v7.6.0) +> [!NOTE] +> `7.6.0` is available on [π PyPI](https://pypi.org/project/click-extra/7.6.0/) and [π GitHub](https://github.com/kdeldycke/click-extra/releases/tag/v7.6.0). + - Add `_default_subcommands` reserved configuration key to auto-invoke subcommands when none are provided on the CLI. Closes {issue}`1405`. - Add `_prepend_subcommands` reserved configuration key to always prepend subcommands to every invocation (requires `chain=True`). Closes {issue}`1405`. - Add `--validate-config` option to validate configuration files. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/click-extra-7.6.3/citation.cff new/click-extra-7.6.4/citation.cff --- old/click-extra-7.6.3/citation.cff 2026-03-02 10:52:22.000000000 +0100 +++ new/click-extra-7.6.4/citation.cff 2026-03-04 15:50:27.000000000 +0100 @@ -8,8 +8,8 @@ email: [email protected] orcid: "https://orcid.org/0000-0001-9748-9014" doi: 10.5281/zenodo.7116050 -version: 7.6.3 +version: 7.6.4 # The release date is kept up to date by the external workflows. See: # https://github.com/kdeldycke/workflows/blob/33b704b489c1aa18b7b7efbf963e153e91e1c810/.github/workflows/changelog.yaml#L135-L137 -date-released: 2026-03-02 +date-released: 2026-03-04 url: "https://github.com/kdeldycke/click-extra" \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/click-extra-7.6.3/click_extra/__init__.py new/click-extra-7.6.4/click_extra/__init__.py --- old/click-extra-7.6.3/click_extra/__init__.py 2026-03-02 10:52:22.000000000 +0100 +++ new/click-extra-7.6.4/click_extra/__init__.py 2026-03-04 15:50:27.000000000 +0100 @@ -242,7 +242,7 @@ """ -__version__ = "7.6.3" +__version__ = "7.6.4" def __getattr__(name: str) -> object: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/click-extra-7.6.3/click_extra/version.py new/click-extra-7.6.4/click_extra/version.py --- old/click-extra-7.6.3/click_extra/version.py 2026-03-02 10:52:22.000000000 +0100 +++ new/click-extra-7.6.4/click_extra/version.py 2026-03-04 15:50:27.000000000 +0100 @@ -259,12 +259,23 @@ # which the user's CLI is implemented. return frame - # Our heuristics to locate the CLI implementation failed. + # Our heuristics to locate the CLI implementation failed. Fall back to + # the outermost frame in the stack. This happens in Nuitka-compiled + # binaries where the entry point module's ``__name__`` may be a + # submodule of the Click ecosystem package (e.g. + # ``click_extra.__main__``) and all frames get skipped. logger = logging.getLogger("click_extra") count_size = len(str(len(frame_chain))) for counter, (p_name, f_name) in enumerate(frame_chain): logger.debug(f"Frame {counter:<{count_size}} # {p_name}:{f_name}") - raise RuntimeError("Could not find the frame in which the CLI is implemented.") + + # The outermost frame is the last one returned by inspect.stack(). + outermost = inspect.stack()[-1].frame + logger.debug( + "cli_frame heuristics exhausted, falling back to outermost frame: " + f"{outermost.f_globals.get('__name__')}:{outermost.f_code.co_name}" + ) + return outermost @cached_property def module(self) -> ModuleType: @@ -371,6 +382,21 @@ callback_globals = getattr(callback, "__globals__", {}) version = callback_globals.get("__version__") + # If still not found, check the parent package. This handles + # ``__main__`` entry points where ``__version__`` is defined in + # the package's ``__init__.py`` (e.g. Nuitka-compiled binaries). + # Skip modules belonging to the Click ecosystem because + # ``cli_frame()`` may resolve to a CliRunner frame instead of + # the user's module, producing false-positive lookups. + if ( + version is None + and self.package_name + and self.module_name.split(".")[0] not in ("click", "click_extra", "cloup") + ): + parent = sys.modules.get(self.package_name) + if parent: + version = getattr(parent, "__version__", None) + if version is not None and not isinstance(version, str): raise ValueError( f"Module version {version!r} expected to be a string or None." @@ -519,17 +545,18 @@ # Replace only the string literal in-place using AST # positions, preserving surrounding content and quoting. - lines = source.splitlines(keepends=True) - line = lines[node.value.end_lineno - 1] - # end_col_offset points past the closing quote. + end_lineno = node.value.end_lineno col_end = node.value.end_col_offset + assert end_lineno is not None and col_end is not None + lines = source.splitlines(keepends=True) + line = lines[end_lineno - 1] # The closing quote character. quote = line[col_end - 1] # Insert the local version just before the closing quote. new_line = ( line[: col_end - 1] + "+" + local_version + quote + line[col_end:] ) - lines[node.value.end_lineno - 1] = new_line + lines[end_lineno - 1] = new_line file_path.write_text("".join(lines), encoding="utf-8") logging.info( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/click-extra-7.6.3/docs/version.md new/click-extra-7.6.4/docs/version.md --- old/click-extra-7.6.3/docs/version.md 2026-03-02 10:52:22.000000000 +0100 +++ new/click-extra-7.6.4/docs/version.md 2026-03-04 15:50:27.000000000 +0100 @@ -84,6 +84,15 @@ The ``git_*`` variables are evaluated at runtime by calling ``git``. They return ``None`` in environments where Git is not available (e.g., standalone Nuitka binaries, Docker containers without Git). This is distinct from ``{version}``, which can be [pre-baked at build time](#pre-baked-versions) to include the commit hash. ``` +```{hint} +The `{version}` variable is resolved in this order: + +1. A `__version__` variable defined alongside your CLI (see [standalone scripts](#standalone-script)). +2. A `__version__` variable in the parent package's `__init__.py` (for `__main__` entry points, e.g. Nuitka-compiled binaries). +3. The version from package metadata via [`importlib.metadata`](https://docs.python.org/3/library/importlib.metadata.html#distribution-versions) β this is the most common source for installed packages. +4. `None` if none of the above succeeds (e.g. unpackaged scripts without `__version__`). +``` + ```{error} Some Click's built-in variables are not recognized: - `%(package)s` should be replaced by `{package_name}` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/click-extra-7.6.3/pyproject.toml new/click-extra-7.6.4/pyproject.toml --- old/click-extra-7.6.3/pyproject.toml 2026-03-02 10:52:22.000000000 +0100 +++ new/click-extra-7.6.4/pyproject.toml 2026-03-04 15:50:27.000000000 +0100 @@ -5,7 +5,7 @@ [project] # Docs: https://packaging.python.org/en/latest/guides/writing-pyproject-toml/ name = "click-extra" -version = "7.6.3" +version = "7.6.4" description = "π Drop-in replacement for Click to make user-friendly and colorful CLI" readme = "readme.md" keywords = [ @@ -180,7 +180,7 @@ [project.urls] "Homepage" = "https://github.com/kdeldycke/click-extra" -"Download" = "https://github.com/kdeldycke/click-extra/releases/tag/v7.6.3" +"Download" = "https://github.com/kdeldycke/click-extra/releases/tag/v7.6.4" "Changelog" = "https://github.com/kdeldycke/click-extra/blob/main/changelog.md" "Issues" = "https://github.com/kdeldycke/click-extra/issues" "Repository" = "https://github.com/kdeldycke/click-extra" @@ -305,6 +305,9 @@ [tool.pytest] # https://docs.pytest.org/en/latest/customize.html#pyproject-toml +markers = [ + "once: Tests that only need to run once, not across the full CI matrix.", +] addopts = [ "--durations=10", "--cov", @@ -325,7 +328,7 @@ report.precision = 2 [tool.bumpversion] -current_version = "7.6.3" +current_version = "7.6.4" allow_dirty = true ignore_missing_files = true # Parse versions with an optional .devN suffix (PEP 440). @@ -391,6 +394,8 @@ { ignore_missing_imports = true, module = [ "sphinx.*" ], follow_imports = "skip" }, ] +[tool.repomatic] + [tool.gha-utils] workflow-sync-exclude = [ "debug.yaml" ] dependency-graph-all-groups = false diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/click-extra-7.6.3/tests/cli-test-plan.yaml new/click-extra-7.6.4/tests/cli-test-plan.yaml --- old/click-extra-7.6.3/tests/cli-test-plan.yaml 2026-03-02 10:52:22.000000000 +0100 +++ new/click-extra-7.6.4/tests/cli-test-plan.yaml 2026-03-04 15:50:27.000000000 +0100 @@ -1,11 +1,25 @@ # This test plan checks some high-level behavior of the click-extra CLI. +# +# The CLI's prog_name is "demo" (from the @group-decorated function name in cli.py). +# The version regex allows stable (1.2.3), dev (1.2.3.dev0), and prebaked +# (1.2.3.dev0+abc1234) versions. -# Check click-extra is reporting the correct version of itself. +# Diagnostic: check if binary produces ANY output on stderr (no stdout assertion). +# This helps debug Nuitka onefile binaries where stdout may be empty. +- cli_parameters: + - --verbosity + - DEBUG + - --version + exit_code: 0 + stderr_regex_matches: "." + + +# Check the CLI reports its version on stdout. - cli_parameters: --version exit_code: 0 - stdout_contains: "\x1b[97mclick-extra\x1b[0m, version \x1b[32m" - stdout_regex_fullmatch: "\x1b\\[97mclick-extra\x1b\\[0m, version \x1b\\[32m[0-9]+\\.[0-9]+\\.[0-9]+\x1b\\[0m\n" + stdout_contains: "demo\x1b[0m, version \x1b[32m" + stdout_regex_fullmatch: "\x1b\\[97mdemo\x1b\\[0m, version \x1b\\[32m[0-9]+\\.[0-9]+\\.[0-9]+(\\.[a-z]+[0-9]+(\\+[0-9a-f]+)?)?\x1b\\[0m\n" # Test combination of version and verbosity. @@ -15,43 +29,18 @@ - --version exit_code: 0 stdout_contains: ", version" - stdout_regex_matches: "\x1b\\[97mclick-extra\x1b\\[0m, version " stderr_contains: - "Set <RootLogger root (DEBUG)> to DEBUG." - - "\x1b[34mdebug\x1b[0m: {prog_name} : \x1b[97mclick-extra\x1b[0m\n" - stderr_regex_matches: - - "\x1b\\[34mdebug\x1b\\[0m: {prog_name} : \x1b\\[97mclick-extra\x1b\\[0m\n" -# Test help output. +# Test help output. Use platform-independent assertions: only check parts of the +# help text that are the same across all OSes (the config default path varies, +# and column alignment depends on terminal width). - cli_parameters: - --help + exit_code: 0 strip_ansi: true stdout_contains: - # yamllint disable rule:line-length - - | - Usage: click-extra [OPTIONS] COMMAND [ARGS]... - - Options: - --time / --no-time Measure and print elapsed execution time. [default: - no-time] - --color, --ansi / --no-color, --no-ansi - Strip out all colors and all ANSI codes from output. - [default: color] - --config CONFIG_PATH Location of the configuration file. Supports glob - pattern of local path and remote URL. [default: - ~/Library/Application Support/click-extra/*.toml|*.yaml - |*.yml|*.json|*.json5|*.jsonc|*.hjson|*.ini|*.xml] - --no-config Ignore all configuration files and only use command - line parameters and environment variables. - --show-params Show all CLI parameters, their provenance, defaults and - value, then exit. - --table-format [asciidoc|csv|csv-excel|csv-excel-tab|csv-unix|double-grid|double-outline|fancy-grid|fancy-outline|github|grid|heavy-grid|heavy-outline|html|jira|latex|latex-booktabs|latex-longtable|latex-raw|mediawiki|mixed-grid|mixed-outline|moinmoin|orgtbl|outline|pipe|plain|presto|pretty|psql|rounded-grid|rounded-outline|rst|simple|simple-grid|simple-outline|textile|tsv|unsafehtml|vertical|youtrack] - Rendering style of tables. [default: rounded-outline] - --verbosity LEVEL Either CRITICAL, ERROR, WARNING, INFO, DEBUG. - [default: WARNING] - -v, --verbose Increase the default WARNING verbosity by one level for - each additional repetition of the option. [default: 0] - --version Show the version and exit. - -h, --help Show this message and exit. - # yamllint enable rule:line-length \ No newline at end of file + - "Usage: demo [OPTIONS] COMMAND [ARGS]..." + - "Show the version and exit." + - "Show this message and exit." diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/click-extra-7.6.3/tests/test_colorize.py new/click-extra-7.6.4/tests/test_colorize.py --- old/click-extra-7.6.3/tests/test_colorize.py 2026-03-02 10:52:22.000000000 +0100 +++ new/click-extra-7.6.4/tests/test_colorize.py 2026-03-04 15:50:27.000000000 +0100 @@ -67,6 +67,7 @@ from .conftest import skip_windows_colors [email protected] def test_theme_definition(): """Ensure we do not leave any property we would have inherited from cloup and logging primitives.""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/click-extra-7.6.3/tests/test_commands.py new/click-extra-7.6.4/tests/test_commands.py --- old/click-extra-7.6.3/tests/test_commands.py 2026-03-02 10:52:22.000000000 +0100 +++ new/click-extra-7.6.4/tests/test_commands.py 2026-03-04 15:50:27.000000000 +0100 @@ -50,6 +50,7 @@ ) [email protected] def test_module_root_declarations(): def fetch_root_members(module): """Fetch all members exposed at the module root.""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/click-extra-7.6.3/tests/test_config.py new/click-extra-7.6.4/tests/test_config.py --- old/click-extra-7.6.3/tests/test_config.py 2026-03-02 10:52:22.000000000 +0100 +++ new/click-extra-7.6.4/tests/test_config.py 2026-03-04 15:50:27.000000000 +0100 @@ -32,7 +32,7 @@ is_unix_not_macos, is_windows, ) -from extra_platforms.pytest import unless_unix_not_macos # type: ignore[attr-defined] +from extra_platforms.pytest import unless_unix_not_macos from click_extra import ( NO_CONFIG, @@ -1241,9 +1241,7 @@ if force_posix: # force_posix ignores XDG_CONFIG_HOME and uses ~/.test-cli/. - assert pattern.startswith( - str(Path("~/.test-cli").expanduser().resolve()) - ) + assert pattern.startswith(str(Path("~/.test-cli").expanduser().resolve())) else: # XDG_CONFIG_HOME is resolved into the pattern. assert pattern.startswith(str(custom_config.resolve() / "test-cli")) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/click-extra-7.6.3/tests/test_pygments.py new/click-extra-7.6.4/tests/test_pygments.py --- old/click-extra-7.6.3/tests/test_pygments.py 2026-03-02 10:52:22.000000000 +0100 +++ new/click-extra-7.6.4/tests/test_pygments.py 2026-03-04 15:50:27.000000000 +0100 @@ -22,6 +22,7 @@ from operator import itemgetter from pathlib import Path +import pytest import requests from boltons.strutils import camel2under from boltons.typeutils import issubclass @@ -44,6 +45,7 @@ PROJECT_ROOT = Path(__file__).parent.parent [email protected] def test_ansi_lexers_candidates(tmp_path): """Look into Pygments test suite to find all ANSI lexers candidates. @@ -157,6 +159,7 @@ assert project_entry_points == entry_points [email protected] def test_formatter_entry_points(): entry_points = {} for name in collect_classes(Formatter): @@ -166,6 +169,7 @@ check_entry_points(entry_points, "project", "entry-points", "pygments.formatters") [email protected] def test_filter_entry_points(): entry_points = {} for name in collect_classes(Filter): @@ -175,6 +179,7 @@ check_entry_points(entry_points, "project", "entry-points", "pygments.filters") [email protected] def test_lexer_entry_points(): entry_points = {} for lexer in collect_session_lexers(): @@ -194,24 +199,28 @@ check_entry_points(entry_points, "project", "entry-points", "pygments.lexers") [email protected] def test_registered_formatters(): for klass in collect_classes(Formatter).values(): for alias in klass.aliases: get_formatter_by_name(alias) [email protected] def test_registered_filters(): for name in collect_classes(Filter): entry_id = camel2under(name).replace("_", "-") get_filter_by_name(entry_id) [email protected] def test_registered_lexers(): for klass in collect_classes(Lexer).values(): for alias in klass.aliases: get_lexer_by_name(alias) [email protected] def test_ansi_lexers_doc(): doc_content = PROJECT_ROOT.joinpath("docs/pygments.md").read_text(encoding="utf-8") for lexer in collect_session_lexers(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/click-extra-7.6.3/tests/test_table.py new/click-extra-7.6.4/tests/test_table.py --- old/click-extra-7.6.3/tests/test_table.py 2026-03-02 10:52:22.000000000 +0100 +++ new/click-extra-7.6.4/tests/test_table.py 2026-03-04 15:50:27.000000000 +0100 @@ -34,6 +34,7 @@ from click_extra.pytest import command_decorators [email protected] def test_table_formats_definition(): """Check all table formats are accounted for and properly named.""" # Formats from tabulate. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/click-extra-7.6.3/tests/test_version.py new/click-extra-7.6.4/tests/test_version.py --- old/click-extra-7.6.3/tests/test_version.py 2026-03-02 10:52:22.000000000 +0100 +++ new/click-extra-7.6.4/tests/test_version.py 2026-03-04 15:50:27.000000000 +0100 @@ -25,7 +25,9 @@ from __future__ import annotations +import inspect import re +import sys import click import pytest @@ -241,6 +243,60 @@ assert result.exit_code == 0 +def test_module_version_parent_package_fallback(monkeypatch): + """``module_version`` falls back to parent package's ``__version__``. + + Simulates the Nuitka use-case: a CLI whose module is ``myapp.__main__`` + (no ``__version__``), with the parent package ``myapp`` providing it. + """ + import types + + # Create a fake parent package with __version__. + fake_parent = types.ModuleType("myapp") + fake_parent.__version__ = "1.2.3" + fake_parent.__package__ = "myapp" + + # Create a fake __main__ submodule without __version__. + fake_main = types.ModuleType("myapp.__main__") + fake_main.__package__ = "myapp" + + monkeypatch.setitem(sys.modules, "myapp", fake_parent) + monkeypatch.setitem(sys.modules, "myapp.__main__", fake_main) + + opt = ExtraVersionOption(["--version"]) + # Bypass cli_frame resolution by setting the module directly. + monkeypatch.setattr( + type(opt), + "module", + property(lambda self: fake_main), + ) + + assert opt.module_version == "1.2.3" + + +def test_cli_frame_fallback(monkeypatch): + """``cli_frame()`` falls back to the outermost frame when all frames are + from the Click ecosystem.""" + original_stack = inspect.stack + + def patched_stack(): + """Make every frame look like it belongs to click_extra.""" + frames = original_stack() + for frame_info in frames: + frame_info.frame.f_globals.setdefault("__name__", "") + # Temporarily override __name__ so the heuristic skips all frames. + frame_info.frame.f_globals["__name__"] = ( + "click_extra." + frame_info.function + ) + return frames + + monkeypatch.setattr(inspect, "stack", patched_stack) + + # Should not raise RuntimeError; instead falls back to outermost frame. + frame = ExtraVersionOption.cli_frame() + assert frame is not None + + @pytest.mark.parametrize( "params", (None, "--help", "blah", ("--config", "random.toml")), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/click-extra-7.6.3/uv.lock new/click-extra-7.6.4/uv.lock --- old/click-extra-7.6.3/uv.lock 2026-03-02 10:52:22.000000000 +0100 +++ new/click-extra-7.6.4/uv.lock 2026-03-04 15:50:27.000000000 +0100 @@ -9,7 +9,7 @@ ] [options] -exclude-newer = "2026-02-20T15:19:31.896200888Z" +exclude-newer = "2026-02-23T17:23:32.3307652Z" exclude-newer-span = "P1W" [[package]] @@ -194,7 +194,7 @@ [[package]] name = "click-extra" -version = "7.6.3.dev0" +version = "7.6.4.dev0" source = { editable = "." } dependencies = [ { name = "boltons" }, @@ -532,7 +532,7 @@ version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -1129,14 +1129,14 @@ [[package]] name = "sphinx-autodoc-typehints" -version = "3.6.2" +version = "3.6.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/97/51/6603ed3786a2d52366c66f49bc8afb31ae5c0e33d4a156afcb38d2bac62c/sphinx_autodoc_typehints-3.6.2.tar.gz", hash = "sha256:3d37709a21b7b765ad6e20a04ecefcb229b9eb0007cb24f6ebaa8a4576ea7f06", size = 37574, upload-time = "2026-01-02T21:25:28.216Z" } +sdist = { url = "https://files.pythonhosted.org/packages/64/5f/ebcaed1a67e623e4a7622808a8be6b0fd8344313e185f62e85a26b0ce26a/sphinx_autodoc_typehints-3.6.3.tar.gz", hash = "sha256:6c387b47d9ad5e75b157810af5bad46901f0a22708ed5e4adf466885a9c60910", size = 38288, upload-time = "2026-02-18T04:22:08.384Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/6a/877e8a6ea52fc86d88ce110ebcfe4f8474ff590d8a8d322909673af3da7b/sphinx_autodoc_typehints-3.6.2-py3-none-any.whl", hash = "sha256:9e70bee1f487b087c83ba0f4949604a4630bee396e263a324aae1dc4268d2c0f", size = 20853, upload-time = "2026-01-02T21:25:26.853Z" }, + { url = "https://files.pythonhosted.org/packages/0a/bd/2b853836d152e40a27655828fdc02c5128f294ac452ad9a13424bb7f92fa/sphinx_autodoc_typehints-3.6.3-py3-none-any.whl", hash = "sha256:46ebc68fa85b320d55887a8d836a01e12e3b7744da973e70af8cedc74072aad5", size = 20882, upload-time = "2026-02-18T04:22:07.238Z" }, ] [[package]] @@ -1346,11 +1346,11 @@ [[package]] name = "types-docutils" -version = "0.22.3.20251115" +version = "0.22.3.20260223" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/eb/d7/576ec24bf61a280f571e1f22284793adc321610b9bcfba1bf468cf7b334f/types_docutils-0.22.3.20251115.tar.gz", hash = "sha256:0f79ea6a7bd4d12d56c9f824a0090ffae0ea4204203eb0006392906850913e16", size = 56828, upload-time = "2025-11-15T02:59:57.371Z" } +sdist = { url = "https://files.pythonhosted.org/packages/80/33/92c0129283363e3b3ba270bf6a2b7d077d949d2f90afc4abaf6e73578563/types_docutils-0.22.3.20260223.tar.gz", hash = "sha256:e90e868da82df615ea2217cf36dff31f09660daa15fc0f956af53f89c1364501", size = 57230, upload-time = "2026-02-23T04:11:21.806Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/01/61ac9eb38f1f978b47443dc6fd2e0a3b0f647c2da741ddad30771f1b2b6f/types_docutils-0.22.3.20251115-py3-none-any.whl", hash = "sha256:c6e53715b65395d00a75a3a8a74e352c669bc63959e65a207dffaa22f4a2ad6e", size = 91951, upload-time = "2025-11-15T02:59:56.413Z" }, + { url = "https://files.pythonhosted.org/packages/ba/c7/a4ae6a75d5b07d63089d5c04d450a0de4a5d48ffcb84b95659b22d3885fe/types_docutils-0.22.3.20260223-py3-none-any.whl", hash = "sha256:cc2d6b7560a28e351903db0989091474aa619ad287843a018324baee9c4d9a8f", size = 91969, upload-time = "2026-02-23T04:11:20.966Z" }, ] [[package]] @@ -1445,21 +1445,21 @@ [[package]] name = "werkzeug" -version = "3.1.5" +version = "3.1.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5a/70/1469ef1d3542ae7c2c7b72bd5e3a4e6ee69d7978fa8a3af05a38eca5becf/werkzeug-3.1.5.tar.gz", hash = "sha256:6a548b0e88955dd07ccb25539d7d0cc97417ee9e179677d22c7041c8f078ce67", size = 864754, upload-time = "2026-01-08T17:49:23.247Z" } +sdist = { url = "https://files.pythonhosted.org/packages/61/f1/ee81806690a87dab5f5653c1f146c92bc066d7f4cebc603ef88eb9e13957/werkzeug-3.1.6.tar.gz", hash = "sha256:210c6bede5a420a913956b4791a7f4d6843a43b6fcee4dfa08a65e93007d0d25", size = 864736, upload-time = "2026-02-19T15:17:18.884Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ad/e4/8d97cca767bcc1be76d16fb76951608305561c6e056811587f36cb1316a8/werkzeug-3.1.5-py3-none-any.whl", hash = "sha256:5111e36e91086ece91f93268bb39b4a35c1e6f1feac762c9c822ded0a4e322dc", size = 225025, upload-time = "2026-01-08T17:49:21.859Z" }, + { url = "https://files.pythonhosted.org/packages/4d/ec/d58832f89ede95652fd01f4f24236af7d32b70cab2196dfcc2d2fd13c5c2/werkzeug-3.1.6-py3-none-any.whl", hash = "sha256:7ddf3357bb9564e407607f988f683d72038551200c704012bb9a4c523d42f131", size = 225166, upload-time = "2026-02-19T15:17:17.475Z" }, ] [[package]] name = "xmltodict" -version = "1.0.3" +version = "1.0.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a9/66/d1242ce8748c698e0036837dfbb530480e31f11d3ecf7101cd4e30d29913/xmltodict-1.0.3.tar.gz", hash = "sha256:3bf1f49c7836df34cf6d9cc7e690c4351f7dfff2ab0b8a1988bba4a9b9474909", size = 25170, upload-time = "2026-02-15T04:05:05.728Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/70/80f3b7c10d2630aa66414bf23d210386700aa390547278c789afa994fd7e/xmltodict-1.0.4.tar.gz", hash = "sha256:6d94c9f834dd9e44514162799d344d815a3a4faec913717a9ecbfa5be1bb8e61", size = 26124, upload-time = "2026-02-22T02:21:22.074Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/82/df/5c50d5bfc886cf61e665f34925e70c7d5ab1d39db0bcc4a03f6fdae3cd76/xmltodict-1.0.3-py3-none-any.whl", hash = "sha256:35d65d5c08f2a1121df338a0c4e49ca638480fa7c1b899ded45e0759bf32e40e", size = 13295, upload-time = "2026-02-15T04:05:04.407Z" }, + { url = "https://files.pythonhosted.org/packages/38/34/98a2f52245f4d47be93b580dae5f9861ef58977d73a79eb47c58f1ad1f3a/xmltodict-1.0.4-py3-none-any.whl", hash = "sha256:a4a00d300b0e1c59fc2bfccb53d7b2e88c32f200df138a0dd2229f842497026a", size = 13580, upload-time = "2026-02-22T02:21:21.039Z" }, ]
