diff --git a/docs/conf.py b/docs/conf.py index 43e165380..979b64d22 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -5,6 +5,7 @@ import guzzle_sphinx_theme from pyinfra import __version__, local +from pyinfra.api import metadata copyright = "Nick Barrett {0} — pyinfra v{1}".format( date.today().year, @@ -57,9 +58,42 @@ ] +def rstjinja(app, docname, source): + """ + Render certain pages as a jinja templates. + """ + # this should only be run when building html + if app.builder.format != "html": + return + # We currently only render docs/operations.rst + # and docs/facts.rst as jinja2 templates + if docname in ["operations", "facts"]: + src = source[0] + rendered = app.builder.templates.render_string(src, app.config.html_context) + source[0] = rendered + + def setup(app): + app.connect("source-read", rstjinja) this_dir = path.dirname(path.realpath(__file__)) scripts_dir = path.abspath(path.join(this_dir, "..", "scripts")) + metadata_file = path.abspath(path.join(this_dir, "..", "pyinfra-metadata.toml")) + if not path.exists(metadata_file): + raise ValueError("No pyinfra-metadata.toml in project root") + with open(metadata_file, "r") as file: + metadata_text = file.read() + plugins = metadata.parse_plugins(metadata_text) + + operation_plugins = [p for p in plugins if p.type == "operation"] + fact_plugins = [p for p in plugins if p.type == "fact"] + html_context = { + "operation_plugins": operation_plugins, + "fact_plugins": fact_plugins, + "tags": metadata.ALLOWED_TAGS, + "docs_language": language, + "docs_version": version, + } + app.config.html_context = html_context for auto_docs_name in ("operations", "facts", "apidoc", "connectors"): auto_docs_path = path.join(this_dir, auto_docs_name) diff --git a/docs/facts.rst b/docs/facts.rst index 6d9261a57..47124a8d6 100644 --- a/docs/facts.rst +++ b/docs/facts.rst @@ -35,26 +35,53 @@ You can leverage facts within :doc:`operations ` like this: **Want a new fact?** Check out :doc:`the writing facts guide <./api/operations>`. -Facts, like :doc:`operations `, are namespaced as different modules - shortcuts to each of these can be found in the sidebar. - .. raw:: html - - -.. toctree:: - :maxdepth: 2 - :glob: - - facts/* +
+ +
+ + +
+ + +
+{% for plugin in fact_plugins %} +
+
+
+
+ + {{ plugin.name }} + +

{{ plugin.description }}

+{% for tag in plugin.tags %} + {{ tag.title_case }} +{% endfor %} +
+
+
+{% endfor %} +
+
+ diff --git a/docs/operations.rst b/docs/operations.rst index ba6a6c579..c2ac5aaf6 100644 --- a/docs/operations.rst +++ b/docs/operations.rst @@ -7,50 +7,52 @@ Operations are used to describe changes to make to systems in the inventory. Use .. raw:: html -

Popular operations by category

- -.. admonition:: Basics - :class: note inline - - :doc:`operations/files`, :doc:`operations/server`, :doc:`operations/git`, :doc:`operations/systemd` - -.. admonition:: System Packages - :class: note inline - - :doc:`operations/apt`, :doc:`operations/apk`, :doc:`operations/brew`, :doc:`operations/dnf`, :doc:`operations/yum` - -.. admonition:: Language Packages - :class: note inline - - :doc:`operations/gem`, :doc:`operations/npm`, :doc:`operations/pip` - -.. admonition:: Databases - :class: note inline - - :doc:`operations/postgresql`, :doc:`operations/mysql` - -.. raw:: html - -

All operations alphabetically

- -.. raw:: html - - - -.. toctree:: - :maxdepth: 2 - :glob: - - operations/* +
+ +
+ + +
+ + +
+{% for plugin in operation_plugins %} +
+
+
+
+ + {{ plugin.name }} + +
+

{{ plugin.description }}

+{% for tag in plugin.tags %} + {{ tag.title_case }} +{% endfor %} +
+
+
+{% endfor %} +
+
+ diff --git a/pyinfra-metadata-schema-1.0.0.json b/pyinfra-metadata-schema-1.0.0.json new file mode 100644 index 000000000..1f5385b60 --- /dev/null +++ b/pyinfra-metadata-schema-1.0.0.json @@ -0,0 +1,57 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "pyinfra-metadata Configuration", + "type": "object", + "properties": { + "$schema": { + "type": "string" + }, + "pyinfra": { + "type": "object", + "properties": { + "plugins": { + "type": "object", + "minProperties": 1, + "patternProperties": { + "^.*$": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "operation", + "fact", + "connector" + ] + }, + "path": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "name", + "type", + "path", + "tags" + ], + "additionalProperties": false + } + } + }}, + "required": [ + "plugins" + ], + "additionalProperties": false + } + }, + "required": ["pyinfra"] +} diff --git a/pyinfra-metadata.toml b/pyinfra-metadata.toml new file mode 100644 index 000000000..5e40d92fc --- /dev/null +++ b/pyinfra-metadata.toml @@ -0,0 +1,550 @@ +"$schema" = "./pyinfra-metadata-schema-1.0.0.json" + +[pyinfra.plugins] + +# System Package Managers +[pyinfra.plugins."apk-ops"] +name = "apk" +path = "src/pyinfra/operations/apk.py" +type = "operation" +tags = ["package-manager", "system"] + +[pyinfra.plugins."apk-facts"] +name = "apk" +path = "src/pyinfra/facts/apk.py" +type = "fact" +tags = ["package-manager", "system"] + +[pyinfra.plugins."apt-ops"] +name = "apt" +path = "src/pyinfra/operations/apt.py" +type = "operation" +tags = ["package-manager", "system"] + +[pyinfra.plugins."apt-facts"] +name = "apt" +path = "src/pyinfra/facts/apt.py" +type = "fact" +tags = ["package-manager", "system"] + +[pyinfra.plugins."brew-ops"] +name = "brew" +path = "src/pyinfra/operations/brew.py" +type = "operation" +tags = ["package-manager", "system"] + +[pyinfra.plugins."brew-facts"] +name = "brew" +path = "src/pyinfra/facts/brew.py" +type = "fact" +tags = ["package-manager", "system"] + +[pyinfra.plugins."choco-ops"] +name = "choco" +path = "src/pyinfra/operations/choco.py" +type = "operation" +tags = ["package-manager", "system"] + +[pyinfra.plugins."choco-facts"] +name = "choco" +path = "src/pyinfra/facts/choco.py" +type = "fact" +tags = ["package-manager", "system"] + +[pyinfra.plugins."dnf-ops"] +name = "dnf" +path = "src/pyinfra/operations/dnf.py" +type = "operation" +tags = ["package-manager", "system"] + +[pyinfra.plugins."dnf-facts"] +name = "dnf" +path = "src/pyinfra/facts/dnf.py" +type = "fact" +tags = ["package-manager", "system"] + +[pyinfra.plugins."flatpak-ops"] +name = "flatpak" +path = "src/pyinfra/operations/flatpak.py" +type = "operation" +tags = ["package-manager", "system"] + +[pyinfra.plugins."flatpak-facts"] +name = "flatpak" +path = "src/pyinfra/facts/flatpak.py" +type = "fact" +tags = ["package-manager", "system"] + +[pyinfra.plugins."freebsd-facts"] +name = "freebsd" +path = "src/pyinfra/facts/freebsd.py" +type = "fact" +tags = ["package-manager"] + +[pyinfra.plugins."opkg-ops"] +name = "opkg" +path = "src/pyinfra/operations/opkg.py" +type = "operation" +tags = ["package-manager", "system"] + +[pyinfra.plugins."opkg-facts"] +name = "opkg" +path = "src/pyinfra/facts/opkg.py" +type = "fact" +tags = ["package-manager", "system"] + +[pyinfra.plugins."pacman-ops"] +name = "pacman" +path = "src/pyinfra/operations/pacman.py" +type = "operation" +tags = ["package-manager", "system"] + +[pyinfra.plugins."pacman-facts"] +name = "pacman" +path = "src/pyinfra/facts/pacman.py" +type = "fact" +tags = ["package-manager", "system"] + +[pyinfra.plugins."pkg-ops"] +name = "pkg" +path = "src/pyinfra/operations/pkg.py" +type = "operation" +tags = ["package-manager", "system"] + +[pyinfra.plugins."pkg-facts"] +name = "pkg" +path = "src/pyinfra/facts/pkg.py" +type = "fact" +tags = ["package-manager", "system"] + +[pyinfra.plugins."pkgin-ops"] +name = "pkgin" +path = "src/pyinfra/operations/pkgin.py" +type = "operation" +tags = ["package-manager", "system"] + +[pyinfra.plugins."pkgin-facts"] +name = "pkgin" +path = "src/pyinfra/facts/pkgin.py" +type = "fact" +tags = ["package-manager", "system"] + +[pyinfra.plugins."podman-facts"] +name = "podman" +path = "src/pyinfra/facts/podman.py" +type = "fact" +tags = ["containers"] + +[pyinfra.plugins."snap-ops"] +name = "snap" +path = "src/pyinfra/operations/snap.py" +type = "operation" +tags = ["package-manager", "system"] + +[pyinfra.plugins."snap-facts"] +name = "snap" +path = "src/pyinfra/facts/snap.py" +type = "fact" +tags = ["package-manager", "system"] + +[pyinfra.plugins."xbps-ops"] +name = "xbps" +path = "src/pyinfra/operations/xbps.py" +type = "operation" +tags = ["package-manager", "system"] + +[pyinfra.plugins."xbps-facts"] +name = "xbps" +path = "src/pyinfra/facts/xbps.py" +type = "fact" +tags = ["package-manager", "system"] + +[pyinfra.plugins."yum-ops"] +name = "yum" +path = "src/pyinfra/operations/yum.py" +type = "operation" +tags = ["package-manager", "system"] + +[pyinfra.plugins."yum-facts"] +name = "yum" +path = "src/pyinfra/facts/yum.py" +type = "fact" +tags = ["package-manager", "system"] + +[pyinfra.plugins."zypper-ops"] +name = "zypper" +path = "src/pyinfra/operations/zypper.py" +type = "operation" +tags = ["package-manager", "system"] + +[pyinfra.plugins."zypper-facts"] +name = "zypper" +path = "src/pyinfra/facts/zypper.py" +type = "fact" +tags = ["package-manager", "system"] + +# Language Package Managers +[pyinfra.plugins."pip-ops"] +name = "pip" +path = "src/pyinfra/operations/pip.py" +type = "operation" +tags = ["package-manager", "python"] + +[pyinfra.plugins."pip-facts"] +name = "pip" +path = "src/pyinfra/facts/pip.py" +type = "fact" +tags = ["package-manager", "python"] + +[pyinfra.plugins."pipx-ops"] +name = "pipx" +path = "src/pyinfra/operations/pipx.py" +type = "operation" +tags = ["package-manager", "python"] + +[pyinfra.plugins."pipx-facts"] +name = "pipx" +path = "src/pyinfra/facts/pipx.py" +type = "fact" +tags = ["package-manager", "python"] + +[pyinfra.plugins."cargo-ops"] +name = "cargo" +path = "src/pyinfra/operations/cargo.py" +type = "operation" +tags = ["package-manager", "rust"] + +[pyinfra.plugins."cargo-facts"] +name = "cargo" +path = "src/pyinfra/facts/cargo.py" +type = "fact" +tags = ["package-manager", "rust"] + +[pyinfra.plugins."gem-ops"] +name = "gem" +path = "src/pyinfra/operations/gem.py" +type = "operation" +tags = ["package-manager", "ruby"] + +[pyinfra.plugins."gem-facts"] +name = "gem" +path = "src/pyinfra/facts/gem.py" +type = "fact" +tags = ["package-manager", "ruby"] + +[pyinfra.plugins."git-ops"] +name = "git" +path = "src/pyinfra/operations/git.py" +type = "operation" +tags = ["version-control-system"] + +[pyinfra.plugins."git-facts"] +name = "git" +path = "src/pyinfra/facts/git.py" +type = "fact" +tags = ["version-control-system"] + +[pyinfra.plugins."gpg"] +name = "gpg" +path = "src/pyinfra/facts/gpg.py" +type = "fact" +tags = ["security"] + +[pyinfra.plugins."npm-ops"] +name = "npm" +path = "src/pyinfra/operations/npm.py" +type = "operation" +tags = ["package-manager", "javascript"] + +[pyinfra.plugins."npm-facts"] +name = "npm" +path = "src/pyinfra/facts/npm.py" +type = "fact" +tags = ["package-manager", "javascript"] + +# Service Management +[pyinfra.plugins."launchd-ops"] +name = "launchd" +path = "src/pyinfra/operations/launchd.py" +type = "operation" +tags = ["service-management"] + +[pyinfra.plugins."launchd-facts"] +name = "launchd" +path = "src/pyinfra/facts/launchd.py" +type = "fact" +tags = ["service-management", "system"] + +[pyinfra.plugins."systemd-ops"] +name = "systemd" +path = "src/pyinfra/operations/systemd.py" +type = "operation" +tags = ["service-management"] + +[pyinfra.plugins."systemd-facts"] +name = "systemd" +path = "src/pyinfra/facts/systemd.py" +type = "fact" +tags = ["service-management", "system"] + +[pyinfra.plugins."sysvinit-ops"] +name = "sysvinit" +path = "src/pyinfra/operations/sysvinit.py" +type = "operation" +tags = ["service-management"] + +[pyinfra.plugins."sysvinit-facts"] +name = "sysvinit" +path = "src/pyinfra/facts/sysvinit.py" +type = "fact" +tags = ["service-management", "system"] + +[pyinfra.plugins."upstart-ops"] +name = "upstart" +path = "src/pyinfra/operations/upstart.py" +type = "operation" +tags = ["service-management"] + +[pyinfra.plugins."upstart-facts"] +name = "upstart" +path = "src/pyinfra/facts/upstart.py" +type = "fact" +tags = ["service-management", "system"] + +[pyinfra.plugins."bsdinit-ops"] +name = "bsdinit" +path = "src/pyinfra/operations/bsdinit.py" +type = "operation" +tags = ["service-management"] + +[pyinfra.plugins."bsdinit-facts"] +name = "bsdinit" +path = "src/pyinfra/facts/bsdinit.py" +type = "fact" +tags = ["service-management", "system"] + +[pyinfra.plugins."openrc-ops"] +name = "openrc" +path = "src/pyinfra/operations/openrc.py" +type = "operation" +tags = ["service-management"] + +[pyinfra.plugins."openrc-facts"] +name = "openrc" +path = "src/pyinfra/facts/openrc.py" +type = "fact" +tags = ["service-management", "system"] + +[pyinfra.plugins."rpm-facts"] +name = "rpm" +path = "src/pyinfra/facts/rpm.py" +type = "fact" +tags = ["package-manager"] + +[pyinfra.plugins."runit-ops"] +name = "runit" +path = "src/pyinfra/operations/runit.py" +type = "operation" +tags = ["service-management"] + +[pyinfra.plugins."runit-facts"] +name = "runit" +path = "src/pyinfra/facts/runit.py" +type = "fact" +tags = ["service-management", "system"] + +# Containers +[pyinfra.plugins."docker-ops"] +name = "docker" +path = "src/pyinfra/operations/docker.py" +type = "operation" +tags = ["containers"] + +[pyinfra.plugins."docker-facts"] +name = "docker" +path = "src/pyinfra/facts/docker.py" +type = "fact" +tags = ["containers", "system"] + +[pyinfra.plugins."lxd-ops"] +name = "lxd" +path = "src/pyinfra/operations/lxd.py" +type = "operation" +tags = ["containers"] + +[pyinfra.plugins."lxd-facts"] +name = "lxd" +path = "src/pyinfra/facts/lxd.py" +type = "fact" +tags = ["containers", "system"] + +[pyinfra.plugins."vzctl-ops"] +name = "vzctl" +path = "src/pyinfra/operations/vzctl.py" +type = "operation" +tags = ["containers"] + +[pyinfra.plugins."vzctl-facts"] +name = "vzctl" +path = "src/pyinfra/facts/vzctl.py" +type = "fact" +tags = ["containers", "system"] + +# Config Management +[pyinfra.plugins."puppet-ops"] +name = "puppet" +path = "src/pyinfra/operations/puppet.py" +type = "operation" +tags = ["configuration-management"] + +[pyinfra.plugins."puppet-facts"] +name = "puppet" +path = "src/pyinfra/facts/puppet.py" +type = "fact" +tags = ["configuration-management"] + +# Security & System Extras +[pyinfra.plugins."iptables-ops"] +name = "iptables" +path = "src/pyinfra/operations/iptables.py" +type = "operation" +tags = ["security"] + +[pyinfra.plugins."iptables-facts"] +name = "iptables" +path = "src/pyinfra/facts/iptables.py" +type = "fact" +tags = ["security", "system"] + +[pyinfra.plugins."selinux-ops"] +name = "selinux" +path = "src/pyinfra/operations/selinux.py" +type = "operation" +tags = ["security"] + +[pyinfra.plugins."selinux-facts"] +name = "selinux" +path = "src/pyinfra/facts/selinux.py" +type = "fact" +tags = ["security", "system"] + +[pyinfra.plugins."efibootmgr-facts"] +name = "efibootmgr" +path = "src/pyinfra/facts/efibootmgr.py" +type = "fact" +tags = ["boot", "system"] + +# Databases +[pyinfra.plugins."mysql-ops"] +name = "mysql" +path = "src/pyinfra/operations/mysql.py" +type = "operation" +tags = ["database"] + +[pyinfra.plugins."mysql-facts"] +name = "mysql" +path = "src/pyinfra/facts/mysql.py" +type = "fact" +tags = ["database", "system"] + +[pyinfra.plugins."postgres-ops"] +name = "postgres" +path = "src/pyinfra/operations/postgres.py" +type = "operation" +tags = ["database"] + +[pyinfra.plugins."postgres-facts"] +name = "postgres" +path = "src/pyinfra/facts/postgres.py" +type = "fact" +tags = ["database", "system"] + +[pyinfra.plugins."postgresql-ops"] +name = "postgresql" +path = "src/pyinfra/operations/postgresql.py" +type = "operation" +tags = ["database"] + +[pyinfra.plugins."postgresql-facts"] +name = "postgresql" +path = "src/pyinfra/facts/postgresql.py" +type = "fact" +tags = ["database", "system"] + +# Storage +[pyinfra.plugins."zfs-ops"] +name = "zfs" +path = "src/pyinfra/operations/zfs.py" +type = "operation" +tags = ["storage"] + +[pyinfra.plugins."zfs-facts"] +name = "zfs" +path = "src/pyinfra/facts/zfs.py" +type = "fact" +tags = ["storage", "system"] + +# General System +[pyinfra.plugins."server-ops"] +name = "server" +path = "src/pyinfra/operations/server.py" +type = "operation" +tags = ["system"] + +[pyinfra.plugins."server-facts"] +name = "server" +path = "src/pyinfra/facts/server.py" +type = "fact" +tags = ["system"] + +[pyinfra.plugins."hardware-facts"] +name = "hardware" +path = "src/pyinfra/facts/hardware.py" +type = "fact" +tags = ["system"] + +[pyinfra.plugins."files-ops"] +name = "files" +path = "src/pyinfra/operations/files.py" +type = "operation" +tags = ["system"] + +[pyinfra.plugins."files-facts"] +name = "files" +path = "src/pyinfra/facts/files.py" +type = "fact" +tags = ["system"] + +[pyinfra.plugins."crontab-ops"] +name = "crontab" +path = "src/pyinfra/operations/crontab.py" +type = "operation" +tags = ["system"] + +[pyinfra.plugins."crontab-facts"] +name = "crontab" +path = "src/pyinfra/facts/crontab.py" +type = "fact" +tags = ["system"] + +[pyinfra.plugins."deb-facts"] +name = "deb" +path = "src/pyinfra/facts/deb.py" +type = "fact" +tags = ["package-manager"] + +#17d14 +#31d27 +#34d29 +# +[pyinfra.plugins."ssh-ops"] +name = "ssh" +path = "src/pyinfra/operations/ssh.py" +type = "operation" +tags = ["system"] + +[pyinfra.plugins."python-ops"] +name = "python" +path = "src/pyinfra/operations/python.py" +type = "operation" +tags = ["system"] diff --git a/pyproject.toml b/pyproject.toml index 9edca8b92..dbf90d0d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,7 @@ dependencies = [ "typeguard>=4,<5", "distro>=1.6,<2", "packaging>=16.1", + "pydantic>=2.11,<3", "typing-extensions; python_version < '3.11'", # Backport of typing for Unpack (added 3.11) ] classifiers = [ diff --git a/scripts/build-public-docs.sh b/scripts/build-public-docs.sh index 17bb6cda2..0fc60e64b 100755 --- a/scripts/build-public-docs.sh +++ b/scripts/build-public-docs.sh @@ -2,36 +2,60 @@ set -euo pipefail -# Generates /en/next NEXT_BRANCH="3.x" -# Generates /en/latest AND redirects /page -> /en/$NAME LATEST_BRANCH="3.x" - BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD) TAG_NAME=$(git tag --points-at HEAD) echo "branch=${BRANCH_NAME}" -echo "tag=${TAG_NAME}" +echo "tag=${TAG_NAME:-}" + +build_docs() { + local docs_version=$1 + local build_dir=$2 + + echo "Building docs for version: ${docs_version}" + rm -rf "$build_dir" + DOCS_VERSION=$docs_version uv run sphinx-build -a docs/ "$build_dir" +} +copy_docs() { + local build_dir=$1 + local output_dir=$2 + + mkdir -p "$output_dir" + cp -r "$build_dir"/* "$output_dir/" +} + +# Build "next" docs if [ "${BRANCH_NAME}" = "${NEXT_BRANCH}" ]; then - echo "Building next docs (${NEXT_BRANCH})" - env DOCS_VERSION=next uv run sphinx-build -a docs/ docs/public/en/next/ + build_docs "next" "build/docs" + copy_docs "build/docs" "docs/public/en/next" fi +# Build "latest" docs if [ "${BRANCH_NAME}" = "${LATEST_BRANCH}" ]; then - echo "Building latest docs (${LATEST_BRANCH})" - env DOCS_VERSION=latest uv run sphinx-build -a docs/ docs/public/en/latest/ + build_docs "latest" "build/docs" + copy_docs "build/docs" "docs/public/en/latest" fi -if [ -n "${TAG_NAME}" ] && [[ "$TAG_NAME" =~ ^v[0-9]\.[0-9]+([\.a-z0-9]+)?$ ]]; then - echo "Building ${BRANCH_NAME} docs for tag: ${TAG_NAME}" - env DOCS_VERSION=$BRANCH_NAME uv run sphinx-build -a docs/ docs/public/en/$BRANCH_NAME/ +# Build versioned docs for a valid tag +if [ -n "${TAG_NAME}" ] && [[ "$TAG_NAME" =~ ^v[0-9]+\.[0-9]+([\.a-z0-9]+)?$ ]]; then + build_docs "$BRANCH_NAME" "build/docs" + copy_docs "build/docs" "docs/public/en/${BRANCH_NAME}" if [ "${BRANCH_NAME}" = "${LATEST_BRANCH}" ]; then echo "Generating /page redirects" - env DOCS_VERSION=$BRANCH_NAME uv run python scripts/generate_redirect_pages.py + DOCS_VERSION=$BRANCH_NAME uv run python scripts/generate_redirect_pages.py fi fi -echo "Docs build complete" +# Local build only to build/docs +if [[ -z "${TAG_NAME}" ]] && [[ "${BRANCH_NAME}" != "${LATEST_BRANCH}" && "${BRANCH_NAME}" != "${NEXT_BRANCH}" ]]; then + echo "Performing local build for branch: ${BRANCH_NAME}" + build_docs "$BRANCH_NAME" "build/docs" + echo "Docs built to build/docs/" + echo "You can open build/docs/index.html or run:" + echo " python3 -m http.server -d build/docs" +fi diff --git a/scripts/generate_facts_docs.py b/scripts/generate_facts_docs.py index 45d536f78..1af23e45a 100755 --- a/scripts/generate_facts_docs.py +++ b/scripts/generate_facts_docs.py @@ -16,7 +16,7 @@ def build_facts_docs(): this_dir = path.dirname(path.realpath(__file__)) docs_dir = path.abspath(path.join(this_dir, "..", "docs")) - facts_dir = path.join(this_dir, "..", "pyinfra", "facts", "*.py") + facts_dir = path.join(this_dir, "..", "src", "pyinfra", "facts", "*.py") makedirs(path.join(docs_dir, "facts"), exist_ok=True) diff --git a/scripts/generate_operations_docs.py b/scripts/generate_operations_docs.py index 75d20a675..1bb295e10 100755 --- a/scripts/generate_operations_docs.py +++ b/scripts/generate_operations_docs.py @@ -18,7 +18,7 @@ def build_operations_docs(): this_dir = path.dirname(path.realpath(__file__)) docs_dir = path.abspath(path.join(this_dir, "..", "docs")) - operations_dir = path.join(this_dir, "..", "pyinfra", "operations", "*.py") + operations_dir = path.join(this_dir, "..", "src", "pyinfra", "operations", "*.py") makedirs(path.join(docs_dir, "operations"), exist_ok=True) diff --git a/src/pyinfra/api/metadata.py b/src/pyinfra/api/metadata.py new file mode 100644 index 000000000..9b2245d51 --- /dev/null +++ b/src/pyinfra/api/metadata.py @@ -0,0 +1,69 @@ +""" +Support parsing pyinfra-metadata.toml + +Currently just parses plugins and their metadata. +""" + +import tomllib +from typing import Literal, get_args + +from pydantic import BaseModel, TypeAdapter, field_validator + +AllowedTagType = Literal[ + "boot", + "containers", + "database", + "service-management", + "package-manager", + "python", + "ruby", + "javascript", + "configuration-management", + "security", + "storage", + "system", + "rust", + "version-control-system", +] + + +class Tag(BaseModel): + """Representation of a plugin tag.""" + + value: AllowedTagType + + @field_validator("value", mode="before") + def _validate_value(cls, v) -> AllowedTagType: + allowed_tags = set(get_args(AllowedTagType)) + if v not in allowed_tags: + raise ValueError(f"Invalid tag: {v}. Allowed: {allowed_tags}") + return v + + @property + def title_case(self) -> str: + return " ".join([t.title() for t in self.value.split("-")]) + + +ALLOWED_TAGS = [Tag(value=tag) for tag in set(get_args(AllowedTagType))] + + +class Plugin(BaseModel): + """Representation of a pyinfra plugin.""" + + name: str + # description: str # FUTURE we should grab these from doc strings + path: str + type: Literal["operation", "fact", "connector", "deploy"] + tags: list[Tag] + + @field_validator("tags", mode="before") + def _wrap_tags(cls, v): + return [Tag(value=tag) if not isinstance(tag, Tag) else tag for tag in v] + + +def parse_plugins(metadata_text: str) -> list[Plugin]: + """Given the contents of a pyinfra-metadata.toml parse out the plugins.""" + pyinfra_metadata = tomllib.loads(metadata_text).get("pyinfra", None) + if not pyinfra_metadata: + raise ValueError("Missing [pyinfra.plugins] section in pyinfra-metadata.toml") + return TypeAdapter(list[Plugin]).validate_python(pyinfra_metadata["plugins"].values()) diff --git a/uv.lock b/uv.lock index 4aad0d357..44e5a30df 100644 --- a/uv.lock +++ b/uv.lock @@ -20,6 +20,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929, upload-time = "2024-07-26T18:15:02.05Z" }, ] +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + [[package]] name = "appdirs" version = "1.4.4" @@ -1133,6 +1142,108 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" }, ] +[[package]] +name = "pydantic" +version = "2.11.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ff/5d/09a551ba512d7ca404d785072700d3f6727a02f6f3c24ecfd081c7cf0aa8/pydantic-2.11.9.tar.gz", hash = "sha256:6b8ffda597a14812a7975c90b82a8a2e777d9257aba3453f973acd3c032a18e2", size = 788495, upload-time = "2025-09-13T11:26:39.325Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/d3/108f2006987c58e76691d5ae5d200dd3e0f532cb4e5fa3560751c3a1feba/pydantic-2.11.9-py3-none-any.whl", hash = "sha256:c42dd626f5cfc1c6950ce6205ea58c93efa406da65f479dcb4029d5934857da2", size = 444855, upload-time = "2025-09-13T11:26:36.909Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.33.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/92/b31726561b5dae176c2d2c2dc43a9c5bfba5d32f96f8b4c0a600dd492447/pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8", size = 2028817, upload-time = "2025-04-23T18:30:43.919Z" }, + { url = "https://files.pythonhosted.org/packages/a3/44/3f0b95fafdaca04a483c4e685fe437c6891001bf3ce8b2fded82b9ea3aa1/pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d", size = 1861357, upload-time = "2025-04-23T18:30:46.372Z" }, + { url = "https://files.pythonhosted.org/packages/30/97/e8f13b55766234caae05372826e8e4b3b96e7b248be3157f53237682e43c/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d", size = 1898011, upload-time = "2025-04-23T18:30:47.591Z" }, + { url = "https://files.pythonhosted.org/packages/9b/a3/99c48cf7bafc991cc3ee66fd544c0aae8dc907b752f1dad2d79b1b5a471f/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572", size = 1982730, upload-time = "2025-04-23T18:30:49.328Z" }, + { url = "https://files.pythonhosted.org/packages/de/8e/a5b882ec4307010a840fb8b58bd9bf65d1840c92eae7534c7441709bf54b/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02", size = 2136178, upload-time = "2025-04-23T18:30:50.907Z" }, + { url = "https://files.pythonhosted.org/packages/e4/bb/71e35fc3ed05af6834e890edb75968e2802fe98778971ab5cba20a162315/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b", size = 2736462, upload-time = "2025-04-23T18:30:52.083Z" }, + { url = "https://files.pythonhosted.org/packages/31/0d/c8f7593e6bc7066289bbc366f2235701dcbebcd1ff0ef8e64f6f239fb47d/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2", size = 2005652, upload-time = "2025-04-23T18:30:53.389Z" }, + { url = "https://files.pythonhosted.org/packages/d2/7a/996d8bd75f3eda405e3dd219ff5ff0a283cd8e34add39d8ef9157e722867/pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a", size = 2113306, upload-time = "2025-04-23T18:30:54.661Z" }, + { url = "https://files.pythonhosted.org/packages/ff/84/daf2a6fb2db40ffda6578a7e8c5a6e9c8affb251a05c233ae37098118788/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac", size = 2073720, upload-time = "2025-04-23T18:30:56.11Z" }, + { url = "https://files.pythonhosted.org/packages/77/fb/2258da019f4825128445ae79456a5499c032b55849dbd5bed78c95ccf163/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a", size = 2244915, upload-time = "2025-04-23T18:30:57.501Z" }, + { url = "https://files.pythonhosted.org/packages/d8/7a/925ff73756031289468326e355b6fa8316960d0d65f8b5d6b3a3e7866de7/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b", size = 2241884, upload-time = "2025-04-23T18:30:58.867Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b0/249ee6d2646f1cdadcb813805fe76265745c4010cf20a8eba7b0e639d9b2/pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22", size = 1910496, upload-time = "2025-04-23T18:31:00.078Z" }, + { url = "https://files.pythonhosted.org/packages/66/ff/172ba8f12a42d4b552917aa65d1f2328990d3ccfc01d5b7c943ec084299f/pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640", size = 1955019, upload-time = "2025-04-23T18:31:01.335Z" }, + { url = "https://files.pythonhosted.org/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584, upload-time = "2025-04-23T18:31:03.106Z" }, + { url = "https://files.pythonhosted.org/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071, upload-time = "2025-04-23T18:31:04.621Z" }, + { url = "https://files.pythonhosted.org/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823, upload-time = "2025-04-23T18:31:06.377Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", size = 1983792, upload-time = "2025-04-23T18:31:07.93Z" }, + { url = "https://files.pythonhosted.org/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", size = 2136338, upload-time = "2025-04-23T18:31:09.283Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", size = 2730998, upload-time = "2025-04-23T18:31:11.7Z" }, + { url = "https://files.pythonhosted.org/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", size = 2003200, upload-time = "2025-04-23T18:31:13.536Z" }, + { url = "https://files.pythonhosted.org/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", size = 2113890, upload-time = "2025-04-23T18:31:15.011Z" }, + { url = "https://files.pythonhosted.org/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d", size = 2073359, upload-time = "2025-04-23T18:31:16.393Z" }, + { url = "https://files.pythonhosted.org/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", size = 2245883, upload-time = "2025-04-23T18:31:17.892Z" }, + { url = "https://files.pythonhosted.org/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", size = 2241074, upload-time = "2025-04-23T18:31:19.205Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", size = 1910538, upload-time = "2025-04-23T18:31:20.541Z" }, + { url = "https://files.pythonhosted.org/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", size = 1952909, upload-time = "2025-04-23T18:31:22.371Z" }, + { url = "https://files.pythonhosted.org/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", size = 1897786, upload-time = "2025-04-23T18:31:24.161Z" }, + { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" }, + { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" }, + { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" }, + { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" }, + { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" }, + { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" }, + { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" }, + { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" }, + { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" }, + { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" }, + { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" }, + { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" }, + { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" }, + { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" }, + { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" }, + { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" }, + { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" }, + { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" }, + { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" }, + { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" }, + { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" }, + { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" }, + { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" }, + { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" }, + { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" }, + { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" }, + { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" }, + { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, + { url = "https://files.pythonhosted.org/packages/30/68/373d55e58b7e83ce371691f6eaa7175e3a24b956c44628eb25d7da007917/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa", size = 2023982, upload-time = "2025-04-23T18:32:53.14Z" }, + { url = "https://files.pythonhosted.org/packages/a4/16/145f54ac08c96a63d8ed6442f9dec17b2773d19920b627b18d4f10a061ea/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29", size = 1858412, upload-time = "2025-04-23T18:32:55.52Z" }, + { url = "https://files.pythonhosted.org/packages/41/b1/c6dc6c3e2de4516c0bb2c46f6a373b91b5660312342a0cf5826e38ad82fa/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d", size = 1892749, upload-time = "2025-04-23T18:32:57.546Z" }, + { url = "https://files.pythonhosted.org/packages/12/73/8cd57e20afba760b21b742106f9dbdfa6697f1570b189c7457a1af4cd8a0/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e", size = 2067527, upload-time = "2025-04-23T18:32:59.771Z" }, + { url = "https://files.pythonhosted.org/packages/e3/d5/0bb5d988cc019b3cba4a78f2d4b3854427fc47ee8ec8e9eaabf787da239c/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c", size = 2108225, upload-time = "2025-04-23T18:33:04.51Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c5/00c02d1571913d496aabf146106ad8239dc132485ee22efe08085084ff7c/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec", size = 2069490, upload-time = "2025-04-23T18:33:06.391Z" }, + { url = "https://files.pythonhosted.org/packages/22/a8/dccc38768274d3ed3a59b5d06f59ccb845778687652daa71df0cab4040d7/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052", size = 2237525, upload-time = "2025-04-23T18:33:08.44Z" }, + { url = "https://files.pythonhosted.org/packages/d4/e7/4f98c0b125dda7cf7ccd14ba936218397b44f50a56dd8c16a3091df116c3/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c", size = 2238446, upload-time = "2025-04-23T18:33:10.313Z" }, + { url = "https://files.pythonhosted.org/packages/ce/91/2ec36480fdb0b783cd9ef6795753c1dea13882f2e68e73bce76ae8c21e6a/pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808", size = 2066678, upload-time = "2025-04-23T18:33:12.224Z" }, + { url = "https://files.pythonhosted.org/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200, upload-time = "2025-04-23T18:33:14.199Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123, upload-time = "2025-04-23T18:33:16.555Z" }, + { url = "https://files.pythonhosted.org/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852, upload-time = "2025-04-23T18:33:18.513Z" }, + { url = "https://files.pythonhosted.org/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", size = 2067484, upload-time = "2025-04-23T18:33:20.475Z" }, + { url = "https://files.pythonhosted.org/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", size = 2108896, upload-time = "2025-04-23T18:33:22.501Z" }, + { url = "https://files.pythonhosted.org/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", size = 2069475, upload-time = "2025-04-23T18:33:24.528Z" }, + { url = "https://files.pythonhosted.org/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", size = 2239013, upload-time = "2025-04-23T18:33:26.621Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", size = 2238715, upload-time = "2025-04-23T18:33:28.656Z" }, + { url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757, upload-time = "2025-04-23T18:33:30.645Z" }, +] + [[package]] name = "pyflakes" version = "3.2.0" @@ -1161,6 +1272,7 @@ dependencies = [ { name = "jinja2" }, { name = "packaging" }, { name = "paramiko" }, + { name = "pydantic" }, { name = "python-dateutil" }, { name = "typeguard" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, @@ -1228,6 +1340,7 @@ requires-dist = [ { name = "jinja2", specifier = ">3,<4" }, { name = "packaging", specifier = ">=16.1" }, { name = "paramiko", specifier = ">=2.7,<4" }, + { name = "pydantic", specifier = ">=2.11,<3" }, { name = "python-dateutil", specifier = ">2,<3" }, { name = "typeguard", specifier = ">=4,<5" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, @@ -1722,6 +1835,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ] +[[package]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] + [[package]] name = "urllib3" version = "2.5.0"