Downstream packaging¶

This page is for distribution packagers building click-extra from source, either a PyPI sdist or a Git tag. To install it on your own system, use uv pip install click-extra or your distribution’s package.

Dependencies¶

This is a graph of the Python package’s dependencies. Boxes hold the dependencies it declares directly, drawn as hexagons: one box for the primary set, and one for each extra. Transitive dependencies render outside the boxes, as ovals:

        flowchart LR
    click_extra[["`click-extra`"]]

    subgraph primary-deps [Primary dependencies]
        extra_platforms{{"`extra-platforms`"}}
        cloup{{"`cloup`"}}
        tabulate{{"`tabulate`"}}
        wcmatch{{"`wcmatch`"}}
        click_0{{"`click`"}}
        wcwidth{{"`wcwidth`"}}
    end

    requests(["`requests`"])
    jinja2(["`jinja2`"])
    mkdocs_get_deps(["`mkdocs-get-deps`"])
    colorama(["`colorama`"])
    markdown_it_py(["`markdown-it-py`"])
    python_dateutil(["`python-dateutil`"])
    ghp_import(["`ghp-import`"])
    markdown(["`markdown`"])
    markupsafe(["`markupsafe`"])
    mdit_py_plugins(["`mdit-py-plugins`"])
    mergedeep(["`mergedeep`"])
    pyyaml_env_tag(["`pyyaml-env-tag`"])
    typing_extensions(["`typing-extensions`"])
    alabaster(["`alabaster`"])
    babel(["`babel`"])
    bracex(["`bracex`"])
    certifi(["`certifi`"])
    charset_normalizer(["`charset-normalizer`"])
    idna(["`idna`"])
    imagesize(["`imagesize`"])
    iniconfig(["`iniconfig`"])
    mdurl(["`mdurl`"])
    pathspec(["`pathspec`"])
    platformdirs(["`platformdirs`"])
    pluggy(["`pluggy`"])
    roman_numerals(["`roman-numerals`"])
    six(["`six`"])
    snowballstemmer(["`snowballstemmer`"])
    sphinxcontrib_applehelp(["`sphinxcontrib-applehelp`"])
    sphinxcontrib_devhelp(["`sphinxcontrib-devhelp`"])
    sphinxcontrib_htmlhelp(["`sphinxcontrib-htmlhelp`"])
    sphinxcontrib_jsmath(["`sphinxcontrib-jsmath`"])
    sphinxcontrib_qthelp(["`sphinxcontrib-qthelp`"])
    sphinxcontrib_serializinghtml(["`sphinxcontrib-serializinghtml`"])
    tzdata(["`tzdata`"])
    urllib3(["`urllib3`"])
    watchdog(["`watchdog`"])

    subgraph ext_carapace [--extra carapace]
        pyyaml{{"`pyyaml >=6.0.3`"}}
    end

    subgraph ext_hjson [--extra hjson]
        hjson{{"`hjson >=3.1`"}}
    end

    subgraph ext_json5 [--extra json5]
        json5{{"`json5 >=0.12.1`"}}
    end

    subgraph ext_mkdocs [--extra mkdocs]
        mkdocs{{"`mkdocs >=1.4`"}}
        pymdown_extensions{{"`pymdown-extensions >=10`"}}
        ext_mkdocs_pygments{{"`pygments >=2.14`"}}
    end

    subgraph ext_pygments [--extra pygments]
        ext_pygments_pygments{{"`pygments >=2.14`"}}
    end

    subgraph ext_pytest [--extra pytest]
        pytest{{"`pytest >=9`"}}
    end

    subgraph ext_sphinx [--extra sphinx]
        sphinx{{"`sphinx >=8`"}}
        myst_parser{{"`myst-parser >=5.1`"}}
        packaging{{"`packaging >=23`"}}
        docutils{{"`docutils >=0.20`"}}
        pygments{{"`pygments >=2.18`"}}
    end

    subgraph ext_toml [--extra toml]
        tomlrt{{"`tomlrt >=2.2.10`"}}
    end

    subgraph ext_yaml [--extra yaml]
        ext_yaml_pyyaml{{"`pyyaml >=6.0.3`"}}
    end

    click_extra ==>|" >=8.5.1 "| click_0
    click_extra ==>|" >=4 "| cloup
    click_extra ==>|" >=14 "| extra_platforms
    click_extra ==>|" >=0.10 "| tabulate
    click_extra ==>|" >=10 "| wcmatch
    click_extra ==>|" >=0.8.3 "| wcwidth
    sphinx --> requests
    sphinx --> jinja2
    sphinx --> packaging
    sphinx --> colorama
    sphinx --> docutils
    sphinx --> pygments
    sphinx --> alabaster
    sphinx --> babel
    sphinx --> imagesize
    sphinx --> roman_numerals
    sphinx --> snowballstemmer
    sphinx --> sphinxcontrib_applehelp
    sphinx --> sphinxcontrib_devhelp
    sphinx --> sphinxcontrib_htmlhelp
    sphinx --> sphinxcontrib_jsmath
    sphinx --> sphinxcontrib_qthelp
    sphinx --> sphinxcontrib_serializinghtml
    mkdocs --> pyyaml
    mkdocs --> jinja2
    mkdocs --> mkdocs_get_deps
    mkdocs --> packaging
    mkdocs --> click_0
    mkdocs --> colorama
    mkdocs --> ghp_import
    mkdocs --> markdown
    mkdocs --> markupsafe
    mkdocs --> mergedeep
    mkdocs --> pyyaml_env_tag
    mkdocs --> pathspec
    mkdocs --> watchdog
    myst_parser --> sphinx
    myst_parser --> pyyaml
    myst_parser --> jinja2
    myst_parser --> docutils
    myst_parser --> markdown_it_py
    myst_parser --> mdit_py_plugins
    pytest --> packaging
    pytest --> colorama
    pytest --> pygments
    pytest --> iniconfig
    pytest --> pluggy
    cloup --> click_0
    cloup --> typing_extensions
    pymdown_extensions --> pyyaml
    pymdown_extensions --> markdown
    extra_platforms --> pytest
    tabulate --> wcwidth
    tomlrt --> typing_extensions
    wcmatch --> bracex
    requests --> certifi
    requests --> charset_normalizer
    requests --> idna
    requests --> urllib3
    jinja2 --> markupsafe
    mkdocs_get_deps --> pyyaml
    mkdocs_get_deps --> mergedeep
    mkdocs_get_deps --> platformdirs
    markdown_it_py --> mdurl
    ghp_import --> python_dateutil
    mdit_py_plugins --> markdown_it_py
    pyyaml_env_tag --> pyyaml
    python_dateutil --> six
    python_dateutil --> tzdata
    click_extra -.-> ext_carapace
    click_extra -.-> ext_hjson
    click_extra -.-> ext_json5
    click_extra -.-> ext_mkdocs
    click_extra -.-> ext_pygments
    click_extra -.-> ext_pytest
    click_extra -.-> ext_sphinx
    click_extra -.-> ext_toml
    click_extra -.-> ext_yaml
    ext_mkdocs_pygments -.- pygments
    ext_pygments_pygments -.- pygments
    ext_yaml_pyyaml -.- pyyaml

    click alabaster "https://pypi.org/project/alabaster/" _blank
    click babel "https://pypi.org/project/babel/" _blank
    click bracex "https://pypi.org/project/bracex/" _blank
    click certifi "https://pypi.org/project/certifi/" _blank
    click charset_normalizer "https://pypi.org/project/charset-normalizer/" _blank
    click click_0 "https://pypi.org/project/click/" _blank
    click click_extra "https://pypi.org/project/click-extra/" _blank
    click cloup "https://pypi.org/project/cloup/" _blank
    click colorama "https://pypi.org/project/colorama/" _blank
    click docutils "https://pypi.org/project/docutils/" _blank
    click extra_platforms "https://pypi.org/project/extra-platforms/" _blank
    click ghp_import "https://pypi.org/project/ghp-import/" _blank
    click hjson "https://pypi.org/project/hjson/" _blank
    click idna "https://pypi.org/project/idna/" _blank
    click imagesize "https://pypi.org/project/imagesize/" _blank
    click iniconfig "https://pypi.org/project/iniconfig/" _blank
    click jinja2 "https://pypi.org/project/jinja2/" _blank
    click json5 "https://pypi.org/project/json5/" _blank
    click markdown "https://pypi.org/project/markdown/" _blank
    click markdown_it_py "https://pypi.org/project/markdown-it-py/" _blank
    click markupsafe "https://pypi.org/project/markupsafe/" _blank
    click mdit_py_plugins "https://pypi.org/project/mdit-py-plugins/" _blank
    click mdurl "https://pypi.org/project/mdurl/" _blank
    click mergedeep "https://pypi.org/project/mergedeep/" _blank
    click mkdocs "https://pypi.org/project/mkdocs/" _blank
    click mkdocs_get_deps "https://pypi.org/project/mkdocs-get-deps/" _blank
    click myst_parser "https://pypi.org/project/myst-parser/" _blank
    click packaging "https://pypi.org/project/packaging/" _blank
    click pathspec "https://pypi.org/project/pathspec/" _blank
    click platformdirs "https://pypi.org/project/platformdirs/" _blank
    click pluggy "https://pypi.org/project/pluggy/" _blank
    click pygments "https://pypi.org/project/pygments/" _blank
    click pymdown_extensions "https://pypi.org/project/pymdown-extensions/" _blank
    click pytest "https://pypi.org/project/pytest/" _blank
    click python_dateutil "https://pypi.org/project/python-dateutil/" _blank
    click pyyaml "https://pypi.org/project/pyyaml/" _blank
    click pyyaml_env_tag "https://pypi.org/project/pyyaml-env-tag/" _blank
    click requests "https://pypi.org/project/requests/" _blank
    click roman_numerals "https://pypi.org/project/roman-numerals/" _blank
    click six "https://pypi.org/project/six/" _blank
    click snowballstemmer "https://pypi.org/project/snowballstemmer/" _blank
    click sphinx "https://pypi.org/project/sphinx/" _blank
    click sphinxcontrib_applehelp "https://pypi.org/project/sphinxcontrib-applehelp/" _blank
    click sphinxcontrib_devhelp "https://pypi.org/project/sphinxcontrib-devhelp/" _blank
    click sphinxcontrib_htmlhelp "https://pypi.org/project/sphinxcontrib-htmlhelp/" _blank
    click sphinxcontrib_jsmath "https://pypi.org/project/sphinxcontrib-jsmath/" _blank
    click sphinxcontrib_qthelp "https://pypi.org/project/sphinxcontrib-qthelp/" _blank
    click sphinxcontrib_serializinghtml "https://pypi.org/project/sphinxcontrib-serializinghtml/" _blank
    click tabulate "https://pypi.org/project/tabulate/" _blank
    click tomlrt "https://pypi.org/project/tomlrt/" _blank
    click typing_extensions "https://pypi.org/project/typing-extensions/" _blank
    click tzdata "https://pypi.org/project/tzdata/" _blank
    click urllib3 "https://pypi.org/project/urllib3/" _blank
    click watchdog "https://pypi.org/project/watchdog/" _blank
    click wcmatch "https://pypi.org/project/wcmatch/" _blank
    click wcwidth "https://pypi.org/project/wcwidth/" _blank
    click ext_mkdocs_pygments "https://pypi.org/project/pygments/" _blank
    click ext_pygments_pygments "https://pypi.org/project/pygments/" _blank
    click ext_yaml_pyyaml "https://pypi.org/project/pyyaml/" _blank

    style click_extra stroke-width:3px
    style click_0 stroke-width:3px
    style cloup stroke-width:3px
    style docutils stroke-width:3px
    style extra_platforms stroke-width:3px
    style hjson stroke-width:3px
    style json5 stroke-width:3px
    style mkdocs stroke-width:3px
    style myst_parser stroke-width:3px
    style packaging stroke-width:3px
    style pygments stroke-width:3px
    style pymdown_extensions stroke-width:3px
    style pytest stroke-width:3px
    style pyyaml stroke-width:3px
    style sphinx stroke-width:3px
    style tabulate stroke-width:3px
    style tomlrt stroke-width:3px
    style wcmatch stroke-width:3px
    style wcwidth stroke-width:3px
    style ext_mkdocs_pygments stroke-width:3px,stroke-dasharray:5 5
    style ext_pygments_pygments stroke-width:3px,stroke-dasharray:5 5
    style ext_yaml_pyyaml stroke-width:3px,stroke-dasharray:5 5

    style primary-deps fill:#1565C020,stroke:#42A5F5
    style ext_carapace fill:#7B1FA220,stroke:#BA68C8
    style ext_hjson fill:#7B1FA220,stroke:#BA68C8
    style ext_json5 fill:#7B1FA220,stroke:#BA68C8
    style ext_mkdocs fill:#7B1FA220,stroke:#BA68C8
    style ext_pygments fill:#7B1FA220,stroke:#BA68C8
    style ext_pytest fill:#7B1FA220,stroke:#BA68C8
    style ext_sphinx fill:#7B1FA220,stroke:#BA68C8
    style ext_toml fill:#7B1FA220,stroke:#BA68C8
    style ext_yaml fill:#7B1FA220,stroke:#BA68C8

    

Building¶

The wheel is built with uv_build, declared as the [build-system] backend. Distributions that do not yet package uv-build can fall back to setuptools. The [tool.setuptools] table is a compatibility shim for that case: it declares click_extra’s py.typed marker as package-data so a setuptools.build_meta build ships it, which setuptools otherwise drops when it installs only *.py. uv_build ignores the table entirely.

Test suite¶

Since click-extra 8.6.1, the PyPI sdist ships tests/, docs/ and the .github/ files the tests read, so the suite runs straight from the sdist. Earlier releases shipped no tests; those builds must start from a Git tag tarball instead.

The suite is friendly to a hermetic build sandbox:

  • Network tests are marked. Exclude them with -m "not network": the build sandbox has no outbound network.

  • The local HTTP server binds an address, not a name. The configuration tests serving a config file over HTTP run against pytest-httpserver, whose httpserver_listen_address fixture tests/conftest.py pins to 127.0.0.1. Its localhost default would need a resolver, which a sandbox is entitled to deny: on macOS the Nix one does, and every such test then errored with socket.gaierror. Whether your sandbox also gates the loopback socket itself is a separate question, and may still call for an exemption.

  • Configuration-discovery tests are HOME-independent. The runner fixture pins HOME (and its platform equivalents) to an isolated directory, so the handful of tests asserting on the config-search debug output stay deterministic even where HOME=/homeless-shelter (Guix, Nixpkgs).

  • The MkDocs tests self-skip when their extras are missing. tests/mkdocs/ needs the MkDocs documentation extras (mkdocs, mkdocs-click, pymdown-extensions); tests/mkdocs/conftest.py skips the whole tree through collect_ignore_glob when any of them is absent, so no --ignore=tests/mkdocs is needed.

  • The Sphinx tests self-skip too, when sphinx or myst-parser is absent, the few outside tests/sphinx/ included, and so does tests/sphinx/test_sphinx_matrix.py when no git binary is on PATH. That module is the only one needing a command rather than an import: it walks the tags of throwaway repositories it builds with git init. Adding git to the build inputs runs the tests of that module; leaving it out costs only those.

So a hermetic builder needs one flag, to leave the network tests out:

$ pytest -m "not network"

Run it against the package you just built, not the one the system already carries. [tool.pytest] addopts sets --import-mode=importlib, and the pytest console script puts no working directory on sys.path, so a bare pytest in the source tree imports click_extra from the system site-packages: the tests then check the installed version, or fail to collect when the two disagree. Install the wheel into a virtual environment and call that environment’s interpreter:

$ python -m venv --system-site-packages test-env
$ test-env/bin/python -m installer dist/*.whl
$ test-env/bin/python -m pytest -m "not network"

Several test modules import optional libraries at collection time (hjson, jsonschema, pygments and others) to exercise the matching features. Install them to run the full suite, as the project’s own CI does.

Test helpers for downstream projects¶

click_extra.pytest (installed with the [pytest] extra) provides fixtures reused by projects built on click-extra, notably the runner/invoke fixtures that pin HOME, and assert_output_regex. Platform-skip decorators such as @skip_hermetic_build come from extra-platforms.