From ef8248504600332c36a3dc1ba614ddd9aa0df43e Mon Sep 17 00:00:00 2001 From: Sebastian Tramp Date: Fri, 4 Sep 2026 11:19:27 +0200 Subject: [PATCH 1/3] update to cmem-plugin-template v9.5.0 Replays the template diff onto this repository and refreshes the locked dependencies that come with it. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01UhUJZ7EwjXm7mN2jAdZzvn --- .claude/hooks/template-feedback.py | 212 ++++ .claude/rules/copier-template.md | 57 + .claude/rules/corporate-memory.md | 47 + .claude/settings.json | 40 + .claude/skills/copier-update/SKILL.md | 76 ++ .claude/skills/plugin-documentation/SKILL.md | 164 +++ .claude/skills/plugin-implementation/SKILL.md | 309 +++++ .claude/skills/plugin-testing/SKILL.md | 99 ++ .claude/skills/release/SKILL.md | 87 ++ .claude/skills/template-feedback/SKILL.md | 132 +++ .copier-answers.yml | 2 +- .github/workflows/check.yml | 13 +- .github/workflows/publish.yml | 6 +- .gitignore | 21 +- .gitlab-ci.yml | 101 -- .tasks-plugin.yml | 2 +- .trivyignore | 12 +- CHANGELOG.md | 6 + LICENSE | 2 +- README-public.md | 4 +- README.md | 4 +- Taskfile.yaml | 16 +- poetry.lock | 1024 ++++++++--------- pyproject.toml | 14 +- 24 files changed, 1798 insertions(+), 652 deletions(-) create mode 100644 .claude/hooks/template-feedback.py create mode 100644 .claude/rules/copier-template.md create mode 100644 .claude/rules/corporate-memory.md create mode 100644 .claude/settings.json create mode 100644 .claude/skills/copier-update/SKILL.md create mode 100644 .claude/skills/plugin-documentation/SKILL.md create mode 100644 .claude/skills/plugin-implementation/SKILL.md create mode 100644 .claude/skills/plugin-testing/SKILL.md create mode 100644 .claude/skills/release/SKILL.md create mode 100644 .claude/skills/template-feedback/SKILL.md delete mode 100644 .gitlab-ci.yml diff --git a/.claude/hooks/template-feedback.py b/.claude/hooks/template-feedback.py new file mode 100644 index 0000000..6c89b37 --- /dev/null +++ b/.claude/hooks/template-feedback.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python3 +"""Notice friction with the cmem-plugin-template at the end of a session. + +This is a Claude Code ``Stop`` hook, wired up in ``.claude/settings.json`` and +run through ``task template:feedback-check``. It belongs to the template - see +``.claude/rules/copier-template.md``. + +It says nothing at all unless the session left evidence that a template owned +file got in the way, and then it asks the agent to consider whether that is +worth reporting upstream. Deciding there is nothing to report is a valid +answer; the hook fires at most once per session either way. + +Two rules govern everything below. Print **nothing** unless there is something +to say, because stdout is the hook's JSON channel. And always exit 0, whatever +goes wrong - a broken hook must never keep a session from ending. + +This file is not part of the package and is not covered by ``task check``, so +keep it small, dependency free and readable. +""" + +import json +import re +import subprocess +import sys +import tempfile +from pathlib import Path + +OPT_OUT = Path(".claude") / "no-template-feedback" + +# Paths the template owns. The shipped rules tell the agent not to edit these, +# so a modification here is the strongest available signal that something in +# the template did not fit. +TEMPLATE_OWNED = ( + ".claude/", + ".github/workflows/", + ".gitlab-ci.yml", + ".pre-commit-config.yaml", + "Taskfile.yaml", +) + +# An added line silencing a check. The rules forbid these outright, so a new +# one always means a rule lost an argument with real code. +SILENCER = re.compile(r"^\+.*#\s*(noqa|type:\s*ignore)") + +# A rule code added to a list in pyproject.toml - almost always the ruff +# ignore list growing. +RUFF_RULE = re.compile(r'^\+\s*"[A-Z]{1,4}[0-9]{1,4}"') + +# What copier leaves behind when an update could not be merged. +CONFLICT = re.compile(r"^\+?<{7} ") + +# A changed `_commit` in the copier answers file. Its presence means the +# working tree *is* a copier update, so the template owned files it rewrote +# were not edited by anyone and are not evidence of anything. +COPIER_UPDATE = re.compile(r"^\+_commit:") + +# Colour codes git writes when the user configured `color.diff = always`, which +# it does even when the output is a pipe. +ANSI = re.compile(r"\x1b\[[0-9;]*m") + +TIMEOUT = 10 + + +def git(*args: str) -> str: + """Run a git command and return its plain output, or "" on any failure. + + Colour is both switched off and stripped afterwards. ``color.diff`` is a + common setting, it outranks ``color.ui``, and it paints diff output even + into a pipe - which would leave every pattern above matching nothing at + all, silently. + """ + try: + result = subprocess.run( + ["git", "--no-pager", "-c", "color.ui=false", *args], + capture_output=True, + text=True, + timeout=TIMEOUT, + check=False, + ) + except (OSError, subprocess.SubprocessError): + return "" + return ANSI.sub("", result.stdout) if result.returncode == 0 else "" + + +def attributed(diff: str) -> list[tuple[str, str]]: + """Pair every diff line with the file it belongs to. + + A flat scan of a diff cannot tell a project's own code from a template + owned file that merely *writes about* the thing being looked for - the + rules file discusses `# noqa`, and matching that prose reports the template + to itself. + """ + pairs = [] + path = "" + for line in diff.splitlines(): + if line.startswith("+++ b/"): + # Git pads the path with a tab, and quotes it when it contains + # spaces - which template owned paths do not, in a rendered project. + path = line[6:].split("\t")[0].strip().strip('"') + else: + pairs.append((path, line)) + return pairs + + +def marker_for(session_id: str) -> Path: + """Return the once-per-session marker file for a session id.""" + safe = re.sub(r"[^A-Za-z0-9_-]", "", session_id)[:64] or "unknown" + return Path(tempfile.gettempdir()) / f"cmem-template-feedback-{safe}" + + +def collect_evidence() -> list[str]: + """Return human readable evidence of template friction, newest first.""" + evidence = [] + + status = git("status", "--porcelain") + diff = git("diff", "HEAD") + diff_lines = diff.splitlines() + + # A `copier update` rewrites every template owned path by definition. Do not + # report the act of taking a new template version as friction with it - a + # suppression written by hand while resolving an update conflict is still a + # real finding, and is caught below because it lands in this project's own + # source rather than in a template owned file. + updating = any(COPIER_UPDATE.match(line) for line in diff_lines) + + touched = sorted( + { + path + for line in status.splitlines() + for path in [line[3:].split(" -> ")[-1].strip()] + if path.startswith(TEMPLATE_OWNED) + } + ) + if touched and not updating: + evidence.append(f"template owned files were changed here: {', '.join(touched)}") + + rejects = [ + line[3:].strip() for line in status.splitlines() if line[3:].strip().endswith(".rej") + ] + if rejects: + evidence.append(f"a copier update left rejected hunks behind: {', '.join(rejects)}") + + # Only in code this project actually authored. A `# noqa` inside a template + # owned file was written by the template, so it can never be evidence of + # this project working around anything. + if any( + SILENCER.match(line) + for path, line in attributed(diff) + if not path.startswith(TEMPLATE_OWNED) + ): + evidence.append("a '# noqa' or '# type: ignore' was added") + + # Conflict markers are the opposite case: one inside a template owned file + # is precisely the "the update could not be merged" report worth having. + if any(CONFLICT.match(line) for line in diff_lines): + evidence.append("conflict markers are still in the working tree") + + # `pyproject.toml` is not template owned, but its lint configuration is + # rendered - so during an update a new ignore entry arrived with the + # template rather than being chosen here. + if not updating and any( + RUFF_RULE.match(line) for line in git("diff", "HEAD", "--", "pyproject.toml").splitlines() + ): + evidence.append("a lint rule was added to the ignore list in pyproject.toml") + + return evidence + + +def main() -> None: + """Read the hook payload, and block once if there is something to report.""" + try: + payload = json.load(sys.stdin) + except (json.JSONDecodeError, ValueError, OSError): + return + + if not isinstance(payload, dict) or payload.get("stop_hook_active"): + return + if OPT_OUT.exists(): + return + + marker = marker_for(str(payload.get("session_id", ""))) + if marker.exists(): + return + + evidence = collect_evidence() + if not evidence: + return + + try: + marker.touch() + except OSError: + return + + reason = ( + "Before finishing: this session shows signs that something in the " + "cmem-plugin-template got in the way - " + + "; ".join(evidence) + + ". Consider whether that is a finding other generated projects would " + "share. If it is, use the 'template-feedback' skill to check what has " + "already been decided and to draft an issue for the template, which " + "you must show the user before filing anything. If it is specific to " + "this project, say so in one sentence and stop - that is a complete " + "answer and this check will not ask again in this session." + ) + json.dump({"decision": "block", "reason": reason}, sys.stdout) + + +if __name__ == "__main__": + try: + main() + except Exception: + pass diff --git a/.claude/rules/copier-template.md b/.claude/rules/copier-template.md new file mode 100644 index 0000000..1ae835f --- /dev/null +++ b/.claude/rules/copier-template.md @@ -0,0 +1,57 @@ +# Working in this project + +This repository was generated from the +[cmem-plugin-template](https://github.com/eccenca/cmem-plugin-template) copier +template and stays connected to it through `copier update`. + +## Some files belong to the template + +`Taskfile.yaml`, `.pre-commit-config.yaml`, the pipeline definition for this +project's host (`.github/workflows/` or `.gitlab-ci.yml`) and everything under +`.claude/` are rendered from the template. Edits there are not preserved: +the next `copier update` either reverts them or turns them into a merge +conflict. + +Add project specific build steps to `TaskfileCustom.yaml` instead - note the +`.yaml` spelling, since only that one is included. Project specific agent +instructions belong in `CLAUDE.md`, which the template never writes and which +is read alongside these rules. Personal tool permissions belong in +`.claude/settings.local.json`, which is git-ignored. + +When something in a template owned file is genuinely wrong, fix it upstream in +the template and update, rather than patching the generated copy. The way to +do that is to report it: use the `template-feedback` skill, which checks what +the template has already decided and drafts an issue for you to confirm. Reach +for it when you wanted to edit a template owned file, when a lint or typing +rule had to be worked around, when a `copier update` conflict will recur for +everyone, or when something these rules or the shipped skills claim turns out +to be wrong. A finding that only applies to this project is not template +feedback. + +## Checks are not negotiable + +Run `task check` before considering a change finished. It runs ruff, mypy, +deptry, trivy and pytest, and it is the same suite the pipeline runs. +`task format:fix` repairs formatting and the mechanical lint findings. + +Ruff is configured in `pyproject.toml` with `select = ["ALL"]` and a curated +`ignore` list. Fix what a rule complains about. Do not add `# noqa`, do not +extend the `ignore` list and do not loosen a mypy setting to make a check pass; +if a rule really is wrong for this project, say so and let a human decide. + +A few rules cannot be satisfied at all in the right context, and obeying them +then writes the bug they exist to prevent - `S701` asks for HTML autoescaping, +which corrupts a Jinja template that renders JSON. When that happens, do not +guess: describe why the rule is wrong here and let a human decide. If they +agree, the resolution is a `# noqa` on the offending line with the reason in a +comment above it - never a new entry in the `ignore` list, which would also +silence the rule for code nobody has looked at. Report it with the +`template-feedback` skill too, since a rule that is wrong here is usually wrong +in other projects as well. + +## Every user visible change gets a changelog entry + +`CHANGELOG.md` follows [Keep a Changelog](https://keepachangelog.com/). Add an +entry under `## [Unreleased]` in the matching `### Added`, `### Changed`, +`### Fixed` or `### Removed` section. The trigger is whether a user would +notice, not whether behaviour changed. diff --git a/.claude/rules/corporate-memory.md b/.claude/rules/corporate-memory.md new file mode 100644 index 0000000..affb0af --- /dev/null +++ b/.claude/rules/corporate-memory.md @@ -0,0 +1,47 @@ +# This project is an eccenca Corporate Memory plugin + +The package provides one or more DataIntegration tasks built on +[cmem-plugin-base](https://github.com/eccenca/cmem-plugin-base). A task is a +class decorated with `@Plugin`, registered through `WorkflowPlugin` or +`TransformPlugin`, and configured by `PluginParameter` entries. + +The text a user reads inside Corporate Memory - the plugin label, description +and documentation, and every parameter and action description - follows the +conventions in the `plugin-documentation` skill. Use that skill whenever you +add or edit one of those blocks. + +Tests follow the conventions in the `plugin-testing` skill. + +The code itself - reaching a deployment, logging, the icon, port declarations, +cancellation and progress reporting - follows the `plugin-implementation` +skill. Use it whenever you write or change a task body, its `@Plugin` block or +its ports. + +## Talking to a deployment + +Plugin code reaches a deployment through +[`cmem-client`](https://pypi.org/project/cmem-client/), built from the context +it was handed: `get_client(context)`. **`cmem.cmempy.*` is +deprecated** - existing calls to it are legacy, and no new code should import +it. The `plugin-implementation` skill has the detail. + +Integration tests and the install tasks talk to a real Corporate Memory +deployment, configured through `cmemc` environment variables in `.env`: +`CMEM_BASE_URI`, `OAUTH_CLIENT_ID`, `OAUTH_CLIENT_SECRET` and +`OAUTH_GRANT_TYPE`. Without them, tests marked `needs_cmem` are skipped rather +than failed. + +`TestExecutionContext` and `TestPluginContext` need a deployment as well: they +construct a `TestUserContext`, which fetches a real OAuth token when it is +built. A test that constructs one is an integration test, however little the +plugin itself does, and needs the marker. + +`task check` therefore changes that deployment when `.env` is populated: it +runs the integration tests, which create their own project and assets and +delete them again afterwards. That is expected and +needs no permission, but it is a real deployment other people may be using, so +never point a test at assets you did not create. + +`task install` and `task uninstall` change what is installed in that +deployment. Ask before running them, and never run them to work around a +failing test. diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..81b4dd4 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,40 @@ +{ + "permissions": { + "allow": [ + "Bash(task build:*)", + "Bash(task check:*)", + "Bash(task format:fix:*)", + "Bash(poetry install:*)", + "Bash(poetry run mypy:*)", + "Bash(poetry run pytest:*)", + "Bash(poetry run ruff:*)", + "Bash(gh issue list:*)", + "Bash(gh search issues:*)" + ] + }, + "hooks": { + "PostToolUse": [ + { + "matcher": "Edit|Write", + "hooks": [ + { + "type": "command", + "command": "task format:fix", + "timeout": 180 + } + ] + } + ], + "Stop": [ + { + "hooks": [ + { + "type": "command", + "command": "task template:feedback-check", + "timeout": 30 + } + ] + } + ] + } +} diff --git a/.claude/skills/copier-update/SKILL.md b/.claude/skills/copier-update/SKILL.md new file mode 100644 index 0000000..ab86916 --- /dev/null +++ b/.claude/skills/copier-update/SKILL.md @@ -0,0 +1,76 @@ +--- +name: copier-update +description: Update this project to a newer release of the cmem-plugin-template - run copier update, resolve the conflicts it leaves behind and verify the result. Use when asked to update the template, take a new template version or resolve copier conflicts. +--- + +# Updating to a newer template release + +This project was generated from +[cmem-plugin-template](https://github.com/eccenca/cmem-plugin-template) and +records where it came from in `.copier-answers.yml`. Updating replays the +difference between the template release recorded there and the newest one onto +this repository. + +This is how the project picks up new dependency constraints, new pipeline +steps, lint configuration and agent files. Generated projects intentionally +have no self-updating automation, so nothing arrives any other way. + +## Before updating + +The working tree must be clean and committed - `copier update` writes into the +tree and there is no way to tell its changes from uncommitted ones afterwards. +Work on a branch, not on the main branch. + +Read what is coming first, so the diff is not a surprise: +. +Entries prefixed `github:` or `gitlab:` concern the pipeline, `plugin:` entries +apply only to plugin projects, and nested bullets warn about consequences - +most often a newly enabled lint rule that will start failing checks here. + +## Updating + +```bash +copier update # asks each question again, previous answer as default +copier update --defaults # keeps every previous answer +``` + +Copier needs version 9 or newer. Answer the questions as before unless the +project really has changed; `project_slug` in particular must not change, since +the package name is derived from it. + +## Resolving what it leaves behind + +Copier merges the template's changes into the files as they are here, so any +file this project edited after generation can conflict. Conflicts appear as +ordinary git conflict markers inside the file: + +```text +<<<<<<< before updating +======= +>>>>>>> after updating +``` + +Resolve them by hand and keep the template's version of template owned files - +`Taskfile.yaml`, `.pre-commit-config.yaml`, `.gitlab-ci.yml`, +`.github/workflows/` and `.claude/`. If this project needed a change in one of +them, that change belongs in `TaskfileCustom.yaml`, in `CLAUDE.md`, or upstream +in the template. + +`CHANGELOG.md`, `README.md` and `pyproject.toml` are the opposite case: they +carry real project content, so keep this project's text and take only the parts +the template actually changed, such as a bumped dependency constraint. + +Example files that were deleted after generation can reappear when the template +changes them. Delete them again. + +## After updating + +```bash +poetry update +task check +``` + +A newly enabled lint rule or a bumped dependency can turn checks red here even +though nothing in this project changed. Fix the findings rather than switching +the rule off, and add a `CHANGELOG.md` entry under `## [Unreleased]` describing +what users notice - usually the new dependency versions. diff --git a/.claude/skills/plugin-documentation/SKILL.md b/.claude/skills/plugin-documentation/SKILL.md new file mode 100644 index 0000000..4ef700e --- /dev/null +++ b/.claude/skills/plugin-documentation/SKILL.md @@ -0,0 +1,164 @@ +--- +name: plugin-documentation +description: Write or revise the user-facing text of a DataIntegration task - the @Plugin label, description and documentation, and the descriptions of its parameters and actions. Use whenever a @Plugin, PluginParameter or PluginAction block is added or edited. +--- + +# Documenting a DataIntegration task + +This governs the text a user reads inside eccenca Corporate Memory: the `label`, +`description` and `documentation` of the `@Plugin` decorator, and the +`description` of every `PluginParameter` and `PluginAction`. It does not govern +Python docstrings, error messages or identifiers. Those are read by developers +and follow the ordinary code conventions. + +`documentation` is rendered as Markdown, so backticks, `**bold**` and links all +work. `description` is a single line shown next to the task in the task list. + +## Ground every claim in the code + +Read `execute()` and the constructor before describing what a task does. Never +write behaviour you have not confirmed, and never carry a claim over from the +previous version of the text just because it was already there. + +When the real behaviour is awkward, write it down anyway. A failure that does +not stop the task, a report that keeps only the last of several errors, a +dependency that is silently left in place: users meet these whether or not the +documentation admits them. Do not fix such things during a documentation pass. +Document them accurately, and collect them as findings for the user to turn +into their own work items. + +## The documentation never restates a parameter + +The task documentation and the parameter descriptions have separate jobs. The +documentation explains the task; each parameter description explains that +parameter. Saying the same thing twice means the two will disagree after the +next change. + +A parameter may be named in the documentation only where it changes the *shape* +of the task, because the shape cannot be understood without it: an input port +that appears or disappears, or modes that exclude each other. Even then, say +what happens to the task rather than what the parameter does. + +``` +Wrong - this is the parameter description, moved + If the Manifest JSON parameter is provided, the manifest is read from it + and the input port is removed. If it is left empty, the manifests come + from the input port. + +Right - this is the shape of the task + Manifests arrive on the input port, which is replaced by a parameter when + the manifest is configured on the task itself. +``` + +## Four beats, in this order + +1. **What the task does.** One or two sentences, in the present tense. +2. **What flows through the ports.** What arrives, what leaves, in what form. + When there is no output port, say so and say what that means: the task is a + terminal step and hands nothing on. +3. **Where the task sits.** How it combines with its sibling tasks, and the + chain it usually appears in. +4. **Caveats and surprising behaviour.** What the task trusts without checking, + what it will not do, what a user is likely to get wrong. + +Length follows content. Most tasks land somewhere around fifteen to twenty +lines. Do not pad a simple task to match a complex one, and do not cut a +complex one down to match a simple one. A task with three mutually exclusive +modes genuinely needs more words than one with a single input port. + +## Parameters + +The first sentence says what the parameter controls. Add further sentences only +for interactions a user cannot guess: which other parameter it excludes, which +port it brings into existence, why the default is what it is. + +Boolean parameters start with `If enabled, ...` and describe the enabled state. +Mark a parameter `advanced=True` when a user can reasonably ignore it, and let +the description explain the situation in which they cannot. + +## Choice parameters explain their values in the labels + +The dropdown is where the value is chosen, so that is where the explanation +belongs. Put it in the choice label and keep the description to the one line +that says what the parameter controls. + +```python +IMPORT_CONFLICT_POLICIES = OrderedDict( + [ + (REPLACE, "Replace - delete the installed package, install the new one"), + (SKIP, "Skip - leave the installed package untouched, continue"), + (FAIL, "Fail - abort the task with an error"), + ] +) +``` + +Never spell the values out a second time in the description. If a value needs +more explanation than a label can hold, that belongs in the caveats beat of the +task documentation, not in a bulleted repeat of the dropdown. + +## Cross-references + +Refer to sibling tasks by their label in bold, matching how they appear in the +task list, and do not link them. The label is what the user is looking at, and +nothing can rot. Links to documentation outside the package are fine where a +stable URL exists. + +## Naming the product + +eccenca Marketing governs how the product is named in anything a user can read. + +- The **first mention in each block of user-visible text** is the full name, + **eccenca Corporate Memory**. The block is the unit, not the file: a user may + meet a `documentation` block, a `description` or a README without having seen + any of the others. +- **Plain "Corporate Memory" afterwards.** Repeating the full name in every + sentence reads badly, and naming the product more often than the text needs + reads worse. +- **"CMEM" is never acceptable** in prose, at any position. +- **Identifiers are exempt everywhere** and stay verbatim - `cmem-plugin-base`, + `cmem_plugin_*` module paths, `CMEM_BASE_URI`, `documentation.eccenca.com`. + `cmemc` is exempt for a stronger reason: it is the separately branded eccenca + command line client, a product name of its own rather than an abbreviation, so + it is never expanded. + +In practice a short text - a `description`, a parameter description - is usually +better with no product name in it at all. The task is already running inside the +product, so naming it there spends words on context the reader has, and the +question of which form applies does not come up. The `example_workflow.py` this +project shipped with is the model: its documentation block never names the +product. + +## Vocabulary + +Use one word per concept across the whole package. Two names for the same thing +read as two different things. + +If the repository carries a glossary, apply it and extend it rather than +inventing a synonym. Do not infer terminology from what the existing code says +most often: majority usage is frequently the wrong usage, and counting +occurrences will confidently give you the term the team has been trying to +retire. When no glossary exists and the choice is not obvious, ask. + +Spelling follows the surrounding eccenca libraries, which use American English. + +## Finish the change + +Add a `CHANGELOG.md` entry under `### Changed` in the `## [Unreleased]` +section. The trigger is whether a user would notice, not whether behaviour +changed, so reworded descriptions and restructured documentation both qualify. + +Then check the result as a user would see it, by loading the descriptors rather +than rereading the source: + +```python +from cmem_plugin_base.dataintegration.discovery import discover_plugins + +for plugin in discover_plugins("").plugins: + print(plugin.label, plugin.description) + print(plugin.documentation) + for parameter in plugin.parameters: + print(" ", parameter.label, "-", parameter.description) +``` + +This catches an unrendered f-string, a description that reads well in the +source and badly in a list, and a choice label that is still the bare key. diff --git a/.claude/skills/plugin-implementation/SKILL.md b/.claude/skills/plugin-implementation/SKILL.md new file mode 100644 index 0000000..231baf0 --- /dev/null +++ b/.claude/skills/plugin-implementation/SKILL.md @@ -0,0 +1,309 @@ +--- +name: plugin-implementation +description: Write or change the code of a DataIntegration task - reaching an eccenca Corporate Memory deployment, logging, the plugin icon, declaring ports, honouring cancellation, reporting progress, and writing custom parameter types with autocompletion. Use whenever a WorkflowPlugin or TransformPlugin body, its @Plugin block, its ports or its parameter types are added or edited. +--- + +# Implementing a DataIntegration task + +These are the conventions the eccenca plugin fleet converged on. They are not +style preferences - each one exists because the obvious alternative behaves +worse inside a running workflow. + +## Reaching an eccenca Corporate Memory deployment + +Build the client from the context you were handed rather than from +configuration, using `get_client()`: + +```python +from cmem_plugin_base.dataintegration.client import get_client + +def execute(self, inputs: Sequence[Entities], context: ExecutionContext) -> Entities | None: + client = get_client(context) +``` + +What comes back is a [`cmem-client`](https://pypi.org/project/cmem-client/) +`Client` carrying the executing user's identity. In tests, where there is no +execution context, use `Client.from_env()` instead. + +**`get_client()` and `Client.from_context()` are the same call.** `get_client()` +is `Client.from_context(context=context)` with a guard in front of it: a context +without a `UserContext` raises `Context has no UserContext.` rather than failing +further downstream, and it is reached through `cmem-plugin-base`, which every +plugin already depends on. Prefer it in new code - but a plugin already calling +`Client.from_context()` is correct and must not be "brought in line", since +rewriting it the other way is the direction that loses the check. + +Do not pass `self.log` to the `logger` argument of either call. Both want a +`logging.Logger` and `self.log` only resembles one - see *Logging* below. + +Sub-APIs live under the cmem-client package, for example +`from cmem_client.repositories.graphs import ImportConflictPolicy`. A project +importing from `cmem_client` directly declares `cmem-client = "^1.0.0"`, so that +deptry sees a direct dependency rather than a transitive one. + +**`cmempy` is deprecated.** Do not add imports from `cmem.cmempy.*`, and do not +use `setup_cmempy_user_access()`. Plenty of existing plugins still call it - +that is legacy, not a pattern to copy. `cmem-plugin-base` continues to depend +on `cmem-cmempy` transitively, which does not make it available for new code. + +## Logging + +The base class already provides a logger as `self.log`. Use it: + +```python +self.log.info(f"Fetched {count} records") +``` + +Do not create a module logger with `logging.getLogger(__name__)`. `self.log` is +a `PluginLogger` that routes into DataIntegration under +`plugins.python.`, so its output is visible where an operator looks +for it; a private logger is not. + +`PluginLogger` is **not** a `logging.Logger`, it only resembles one. It offers +`debug()`, `info()`, `warning()` and `error()`, and each takes a single, already +formatted string. The `logging` idiom +`self.log.info("Fetched %s records", count)` raises `TypeError` at runtime, so +an f-string is the form to use - the ruff rule +`G004` (logging statement uses f-string) is in the `ignore` list accordingly. +The same gap is why `self.log` cannot be passed as a `logger` argument. + +## The icon + +Ship an SVG beside the plugin module and reference it by package: + +```python +@Plugin( + label="My task", + icon=Icon(file_name="my_task.svg", package=__package__), + ... +) +``` + +Always `package=__package__` rather than a hard-coded package name - it keeps +working when the module moves or the project is renamed. + +What the SVG contains matters as much as where it lives, and neither +`task check` nor plugin discovery says a word about it - a wrong icon is only +found by somebody looking at the workspace. + +Give the mark no colour of its own: put `fill="currentColor"` (or +`stroke="currentColor"`) on the root `svg` and name no `fill` on the shapes, so +it follows the surrounding text colour and stays legible on a light and a dark +workspace alike. Hard-coding a brand colour produces the one icon in the task +list that looks broken when the theme changes. Leave the background +transparent - no full-size `` - because every other icon in that list is, +and an opaque tile reads as a coloured block among them. + +`cmem-plugin-parameters` and `cmem-plugin-pyshacl` are the icons to copy the +shape of. + +## Declaring ports + +Say what the task accepts and produces; do not leave it implicit. + +```python +input_ports=FixedNumberOfInputs([FixedSchemaPort(schema=MY_SCHEMA)]), +output_port=FixedSchemaPort(schema=MY_SCHEMA), +``` + +Use `UnknownSchemaPort` when the schema is only known at runtime, and +`FlexibleNumberOfInputs` when the task genuinely accepts any number of inputs. +A task that consumes nothing declares `FixedNumberOfInputs([])`. + +## Honouring cancellation + +A long-running task must stop when the user cancels the workflow. Check the +status inside the entity loop, and guard the access: + +```python +from contextlib import suppress + +for entity in inputs[0].entities: + with suppress(AttributeError): + if context.workflow.status() == "Canceling": + break + ... +``` + +The `suppress(AttributeError)` is required, not defensive noise: +`context.workflow` is absent in some contexts - notably the test contexts - and +an unguarded check raises there while working in production. + +## Reporting progress + +Report through the execution context so the workflow UI can show what is +happening: + +```python +context.report.update( + ExecutionReport( + entity_count=processed, + operation="write", + operation_desc="entities written", + ) +) +``` + +- `operation` is a short label. Use **`read`**, **`write`**, **`wait`** or + **`done`**; do not invent new verbs, and do not use past tense. +- `operation_desc` describes the counted thing in plural, so it reads correctly + after the number: `"entities written"`, `"files uploaded"`. +- Update **inside** the loop, not only once at the end. A report emitted after + the work is finished shows a user nothing while the task is running, which is + exactly when they are looking. + +## A constructor with six or more parameters + +Ruff's `PLR0913` and `PLR0917` both complain about a long argument list, and a +task's constructor takes one argument per `PluginParameter` - so its arity is +decided by the configuration surface the task offers, not by a style choice. +Neither escape ruff suggests is available: keyword-only arguments change the +signature DataIntegration instantiates, and folding parameters into one object +breaks the one-argument-per-parameter mapping the framework depends on. + +This is the standing exception the lint rules describe, so it needs no fresh +decision. Suppress both codes on the `def`, with the reason above it: + +```python +# A plugin constructor takes one argument per PluginParameter, so its arity is +# fixed by the plugin's configuration surface, not by a style choice here. +def __init__( # noqa: PLR0913, PLR0917 + self, + ... +``` + +`PLR0917` became a stable rule in ruff 0.16, so a task that passed before may +start failing on it without anything in the task changing. Suppress it on the +constructor only - it stays a real finding on an ordinary function, which is +why it is not in the `ignore` list in `pyproject.toml`. + +## Parameters that carry secrets + +A password, token or API key is typed, never a plain string: + +```python +from cmem_plugin_base.dataintegration.parameter.password import Password, PasswordParameterType + +PluginParameter( + name="api_key", + label="API key", + param_type=PasswordParameterType(), +) +``` + +The value arrives as a `Password`; call `.decrypt()` only where it is used. +Typing it as `str` puts the secret in plain text in the task configuration and +in the project export. + +## Custom parameter types + +A `PluginParameter` without an explicit `param_type` gets one derived from the +constructor's type annotation, and **that derivation cannot handle a union**. +An annotation like `JinjaCode | str` or `str | Password` raises +`TypeError: issubclass() arg 1 must be a class` while the module is imported, +which aborts discovery for the **whole package** - every plugin the package +ships disappears from the workspace, `task check` stays green, and the +traceback only shows up in `PluginDiscoveryResult.errors`. + +So annotate a single type, or pass `param_type` explicitly. Writing a union is +almost always the moment you needed `param_type` anyway: `cmem-plugin-ssh` +declares `private_key: str | Password` and works only because it also passes +`param_type=PasswordParameterType()`. + +Reach for a shipped type first - `ChoiceParameterType`, `GraphParameterType`, +`DatasetParameterType`, `PasswordParameterType`, and the `code`, `multiline` +and `resource` types. Write your own only when the value is a thing the user +should pick from a list that only your plugin can produce: a folder on a +remote host, a collection in a store, a model offered by an API. + +Subclass `StringParameterType`, not `ParameterType` directly - the value is +carried as a string and `StringParameterType` already handles that: + +```python +from typing import Any, ClassVar + +from cmem_plugin_base.dataintegration.context import PluginContext +from cmem_plugin_base.dataintegration.types import Autocompletion, StringParameterType + + +class CollectionParameterType(StringParameterType): + """Autocomplete the collections available on the configured server.""" + + allow_only_autocompleted_values: bool = True + + def autocomplete( + self, + query_terms: list[str], + depend_on_parameter_values: list[Any], + context: PluginContext, + ) -> list[Autocompletion]: + """Return the collections matching all query terms.""" + results = [ + Autocompletion(value=name, label=f"{title} ({name})") + for name, title in self._fetch(context) + ] + if not query_terms: + return results + return [ + r + for r in results + if all(term.lower() in (r.label or r.value).lower() for term in query_terms) + ] +``` + +- An **empty `query_terms` must return everything**. That is the list the user + sees before typing, and returning nothing looks like a broken parameter. +- Match against every term, not any of them - the UI splits what the user typed + on whitespace. +- Return a **stable order**. Sort at the end; do not deduplicate with `set()` + after sorting, because that throws the ordering away again. + +### The flags + +- `allow_only_autocompleted_values = True` makes the parameter a closed + vocabulary: the UI refuses values that did not come from your list. Set it + when a value the server does not know is always an error. +- `autocomplete_value_with_labels = True` tells the UI the labels matter and + must be shown instead of the raw values. +- Implement `label()` when a stored value is not human-readable on its own. A + saved task shows the raw value until `label()` resolves it, so a task + configured last week displays an opaque id without it. + +### Depending on other parameters + +An autocompletion that needs a hostname, or credentials, declares the +parameters it reads: + +```python +autocompletion_depends_on_parameters: ClassVar[list[str]] = [ + "hostname", + "port", + "password", +] +``` + +Their values arrive in `depend_on_parameter_values` **positionally, in exactly +this order** - `depend_on_parameter_values[2]` is `password` only because it is +third in the list. Reordering the list silently repoints every index, so read +them once at the top of `autocomplete()` and unpack by name: + +```python +hostname, port, password = depend_on_parameter_values +``` + +A dependency that is itself a secret arrives as a `Password`, not a string, and +needs `password.decrypt()` before use. + +Until every declared parameter has a value, no autocompletion happens at all - +so keep the list to what you genuinely need. Each extra entry is one more field +the user must fill before the list appears. + +DataIntegration also **clears** a dependent parameter's own value whenever one +of the parameters it declares changes, and that propagates: in a chain of +token -> resource -> sub-resource, changing the token clears the resource, +which clears the sub-resource. Two things follow, and both change how such a +chain is designed. A chained parameter never holds a value left over from an +earlier selection, so there is no stale combination to validate against and no +caveat to write into the task documentation about one. And a chained parameter +can safely be made mandatory, because a user who invalidates it is asked to +choose again rather than being stranded on a value they cannot correct. diff --git a/.claude/skills/plugin-testing/SKILL.md b/.claude/skills/plugin-testing/SKILL.md new file mode 100644 index 0000000..0c9de02 --- /dev/null +++ b/.claude/skills/plugin-testing/SKILL.md @@ -0,0 +1,99 @@ +--- +name: plugin-testing +description: Write or fix tests for a DataIntegration task - what can be tested standalone, when a test needs an eccenca Corporate Memory deployment and how to mark it, and how to create and clean up test assets. Use when adding, changing or debugging tests in this project. +--- + +# Testing a DataIntegration task + +Tests run with `task check:pytest`, which collects coverage and a memray +report. `pytest-dotenv` loads `.env`, so the same eccenca Corporate Memory +credentials the plugin uses are available to the tests. + +## Two kinds of test, and the line between them + +A test that only exercises Python - a transform plugin, a helper, parameter +validation - runs anywhere and must never require credentials. + +Everything else needs a deployment and is marked: + +```python +needs_cmem = pytest.mark.skipif( + os.environ.get("CMEM_BASE_URI", "") == "", + reason="Needs eccenca Corporate Memory configuration", +) +``` + +The marker skips rather than fails, which is what keeps the suite green in a +pipeline without secrets. A missing marker turns a contributor's first +`task check` into a failure they cannot fix, so the marker is not optional. + +**`TestExecutionContext` needs a deployment.** It builds a `TestUserContext`, +which fetches a real OAuth token on construction. Any test that constructs one +is an integration test and must carry `@needs_cmem`, even when the plugin +itself never calls out. The same is true of `TestPluginContext`. + +A test that needs anything *else* - a third party API, a scratch instance to +run integration tests against - declares its own environment variables, named +`TESTING__`, and gates on them the same way: + +```python +needs_hcloud = pytest.mark.skipif( + os.environ.get("TESTING_HCLOUD_TOKEN", "") == "", + reason="Needs a Hetzner Cloud API token", +) +``` + +The prefix is what makes it possible to tell, in a `.env` file or in a +group-level list of CI variables, which entries drive the test suite and which +configure the product. Pick it before writing the `skipif`: the name ends up in +`.env`, in the pipeline configuration and in every marker that reads it, so +renaming it later is a change in three places at once. + +The Corporate Memory connection variables are the exception. `CMEM_BASE_URI`, +`OAUTH_CLIENT_ID`, `OAUTH_CLIENT_SECRET` and `OAUTH_GRANT_TYPE` keep their +established `cmemc` names, because the plugin and `cmemc` read them too - they +configure the product and are not test-only. + +## Testing a workflow plugin + +Construct the plugin with its parameters, call `execute()` with the inputs it +expects, and assert on the entities that come back - their number, their values +and the schema paths they are supposed to match: + +```python +@needs_cmem +def test_execution() -> None: + """Test plugin execution""" + result = MyPlugin(number_of_entities=100).execute(inputs=(), context=TestExecutionContext()) + for entity in result.entities: + assert len(entity.values) == len(result.schema.paths) +``` + +Assert the plugin's own contract as well: that an output port declared as +absent really produces none, and that a parameter combination the documentation +calls exclusive is rejected. + +## Testing a transform plugin + +`transform()` takes a sequence of input value sequences and returns a sequence +of strings. No context, no credentials, so cover the awkward cases here: empty +input, several inputs at once, and the boundaries the implementation actually +computes. + +## Test assets in a deployment + +A test that needs a project, dataset or graph creates it in a fixture, yields, +and deletes it afterwards - including when the test fails. Name assets after +the package so a leftover is traceable and two projects never collide. + +Use `cmem_client` for setup and teardown rather than driving the plugin, so a +broken plugin fails the assertion instead of the fixture. Never point a test at +assets that happen to exist in the deployment: the next person runs the suite +somewhere else. + +## Finishing + +Run `task check:pytest` before finishing, and `task check` before considering +the change done. Tests are not user visible, so a change that only adds or +fixes tests needs no `CHANGELOG.md` entry - the behaviour change that made the +test necessary does. diff --git a/.claude/skills/release/SKILL.md b/.claude/skills/release/SKILL.md new file mode 100644 index 0000000..ec19512 --- /dev/null +++ b/.claude/skills/release/SKILL.md @@ -0,0 +1,87 @@ +--- +name: release +description: Cut a release of this project - check the changelog, rename the Unreleased heading, commit, tag vX.Y.Z and push, which publishes the package to PyPI. Use when asked to release, tag, version or publish this project. +--- + +# Releasing cmem-plugin-validation + +The version is never written into a file. `poetry-dynamic-versioning` derives +it from the git tag, so **the annotated tag is the release**. `pyproject.toml` +keeps `version = "0.0.0"` forever; changing it there does nothing. + +Pushing a tag triggers `.github/workflows/publish.yml`, which builds the +package and publishes it to PyPI with the `PYPI_TOKEN` secret. It fires on +*any* tag, so a tag is never a private bookmark: it either publishes a release +or fails the pipeline. Publish it once and that version is gone - PyPI does not +allow replacing it. + +Accepts an optional version argument, e.g. `/release 1.4.0`. Without one, +derive it in step 2 and confirm with the user before tagging. + +## 1. Preflight + +Stop at the first failure and report it rather than fixing it silently. + +1. **Clean tree.** `git status --porcelain` is empty. +2. **In sync.** `git fetch`, and the current branch is not behind its remote. +3. **Checks pass.** `task check` is green. This is the state being released. +4. **Changelog is ready.** `CHANGELOG.md` has a `## [Unreleased]` heading + followed by at least one `### Added|Changed|Deprecated|Removed|Fixed|Security` + section with at least one bullet. + + Then confirm it is complete, by listing what changed since the last tag: + + ```bash + git log --oneline "$(git describe --tags --abbrev=0)"..HEAD + ``` + + Anything a user would notice needs an entry. If something is missing, add it + before releasing rather than after. + +## 2. Derive the version + +Read the previous version from `git describe --tags --abbrev=0` and the nature +of the change from the `## [Unreleased]` section, following +[Semantic Versioning](https://semver.org/): + +- `### Removed` entries, or a change that breaks existing workflows or + configured parameters, mean a **major** bump. +- `### Added` entries mean a **minor** bump. +- Only `### Fixed`, `### Changed` or `### Security` entries mean a **patch** + bump. + +For a plugin, weigh compatibility from the user's point of view: renaming a +parameter, removing a port or changing a default all break configured tasks in +a live deployment, however small the diff looks. + +State the derived version and the reason, and let the user confirm. + +## 3. Release commit + +Rename the heading in `CHANGELOG.md`, keeping the bullets untouched: + +```markdown +## [1.4.0] 2026-08-21 +``` + +Use today's date in `YYYY-MM-DD`. Do not add a new empty `## [Unreleased]` +section in the same commit - the next change adds it back. + +Commit exactly that change, with the version as the subject: + +```bash +git commit -m "release 1.4.0" CHANGELOG.md +``` + +## 4. Tag and push + +The tag is annotated and named `vX.Y.Z`. Sign it if the user's git is +configured for signing (`git config user.signingkey`): + +```bash +git tag -a -m "release 1.4.0" v1.4.0 +git push && git push --tags +``` + +Then watch the publish workflow at https://github.com/eccenca/cmem-plugin-validation/actions and confirm the +new version appeared on [PyPI](https://pypi.org/project/cmem-plugin-validation/). diff --git a/.claude/skills/template-feedback/SKILL.md b/.claude/skills/template-feedback/SKILL.md new file mode 100644 index 0000000..67e3313 --- /dev/null +++ b/.claude/skills/template-feedback/SKILL.md @@ -0,0 +1,132 @@ +--- +name: template-feedback +description: Report a finding upstream to the cmem-plugin-template - check what has already been decided, draft a GitHub issue and file it after the user confirms. Use when a template owned file is wrong or in the way, when a lint rule had to be worked around, when a copier update conflict will recur for everyone, or when asked to report something to the template. +--- + +# Reporting a finding to the template + +This project is generated from +[cmem-plugin-template](https://github.com/eccenca/cmem-plugin-template) and +receives changes only through `copier update`. Nothing here flows back on its +own, so a finding that would help other generated projects has to be filed as +an issue on the template repository. + +The point is not to log everything that happened. It is to move the small +number of findings that are **about the template** out of this project, where +they die, and into the one place a fix can reach every project. + +## Does this belong upstream? + +Ask whether the finding would still make sense in a project that has nothing +to do with this one. Concretely, upstream material looks like: + +- a lint or typing rule that had to be worked around here and would fire the + same way anywhere +- a step in `Taskfile.yaml` or the pipeline that is missing, wrong or fails + identically everywhere +- a statement in `.claude/rules/` or one of the shipped skills that turned out + to be wrong or out of date +- a `copier update` conflict that every project will hit +- a pattern this project keeps writing by hand that the template could ship + +Anything specific to this project - its business logic, its own dependencies, +a task only it needs - is not template feedback. Put those in +`TaskfileCustom.yaml`, in `CLAUDE.md`, or in this project's own tracker. + +Wishes count. "The template could ship a skill for X" is a legitimate report +as long as it is argued for projects in general, not just this one. + +## Check what has already been decided + +Do this before drafting anything. Most findings have been seen before, and a +duplicate costs a maintainer more than it costs you. + +1. Search the issues, open **and** closed: + + ```bash + gh issue list --repo eccenca/cmem-plugin-template \ + --state all --label template-feedback --search "" + ``` + + An **open** match means comment there instead of opening a second issue. A + **closed** match means it was answered already - read the answer and stop. + +2. Read the *Deliberate decisions - please do not re-raise these* section of + the template's `CLAUDE.md`: + + + That section is the list of findings that were considered and rejected on + purpose - the GitLab `build` job not needing `ruff`, the `pypi` job being + manual, the mypy badge, and others. If the finding is there, it is settled. + Say so and stop. + +3. Skim the template's `CHANGELOG.md` for the `## [Unreleased]` section. It may + already be fixed and waiting for a release, in which case the answer is to + update once that release is out. + +## Draft the issue + +Never file without showing the user the exact title and body first, and never +file without their explicit go-ahead in that turn. This is the only step in +this project that publishes something, it is permanent, and the tracker is +public while most generated projects are not. + +**Do not name this repository, and do not paste code from it.** Many projects +generated from this template are private, some of them customer specific, and +naming one on a public tracker publishes a relationship that was not yours to +publish. Describe the finding in general terms; write a minimal synthetic +reproduction if one helps. If naming the project is genuinely useful, let the +user add it when they confirm - they know whether it is public. + +What the maintainer needs instead of a name, taken from `.copier-answers.yml`: + +- `_commit` - the template version this project was rendered from +- `project_type` - `plugin` or `generic` +- whether `github_page` and `pypi` are answered + +Title: short and imperative, describing the change to the template - not the +symptom here. Body, following the repository's issue form: + +```text +### The finding + + +### Why this generalises + + +### Which part of the template + + +### Suggested change + + +### Environment +Template version: <_commit> +project_type: +github_page answered: +pypi answered: +``` + +## File it + +After the user confirms: + +```bash +gh issue create --repo eccenca/cmem-plugin-template \ + --label template-feedback --title "" --body "<body>" +``` + +This one is not pre-approved in `.claude/settings.json`, so it will ask for +permission. That is deliberate - the prompt is the last checkpoint before +something becomes public. + +If `gh` is missing or not authenticated, do not stall and do not look for +another way to publish it. Print the finished title and body together with the +command above, and let the user file it. They can also use the form directly: +<https://github.com/eccenca/cmem-plugin-template/issues/new?template=template-feedback.yml> + +## Turning it off + +A project that does not want the session end check can create an empty +`.claude/no-template-feedback` file. This skill still works when invoked +directly; only the automatic reminder goes away. diff --git a/.copier-answers.yml b/.copier-answers.yml index cca5442..1ee25b6 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier -_commit: v8.5.0-4-gd5f8597 +_commit: v9.5.0 _src_path: gh:eccenca/cmem-plugin-template author_mail: cmempy-developer@eccenca.com author_name: eccenca GmbH diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index a8e4694..b049176 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -20,21 +20,22 @@ jobs: steps: - name: Check out repository - uses: actions/checkout@v5 + uses: actions/checkout@v7 - name: Cache Trivy DB id: cache-trivydb - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: .trivycache - key: ${{ runner.os }}-trivydb + key: ${{ runner.os }}-trivydb-${{ github.run_id }} + restore-keys: ${{ runner.os }}-trivydb- - name: Install Task - uses: arduino/setup-task@v2 + uses: arduino/setup-task@v3 - name: Set up python id: setup-python - uses: actions/setup-python@v6 + uses: actions/setup-python@v7 with: python-version: '3.13' @@ -73,7 +74,7 @@ jobs: task check:trivy - name: Publish Test Report in Action - uses: mikepenz/action-junit-report@v4 + uses: mikepenz/action-junit-report@v6 if: always() # always run even if the previous step fails with: report_paths: dist/junit-*.xml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 94c9751..e0dbc62 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -17,14 +17,14 @@ jobs: steps: - name: Check out repository - uses: actions/checkout@v5 + uses: actions/checkout@v7 - name: Install Task - uses: arduino/setup-task@v2 + uses: arduino/setup-task@v3 - name: Set up python id: setup-python - uses: actions/setup-python@v6 + uses: actions/setup-python@v7 with: python-version: '3.13' diff --git a/.gitignore b/.gitignore index 845210d..12e3c0c 100644 --- a/.gitignore +++ b/.gitignore @@ -141,11 +141,26 @@ cython_debug/ # Claude code specifics .claude/settings.local.json +# JetBrains IDEs - most of .idea/ is shared, which is what PyCharm's own +# generated .idea/.gitignore already assumes: vcs.xml, modules.xml, the project +# .iml, inspectionProfiles/ and runConfigurations/ describe the project, not the +# machine. These entries follow github/gitignore's Global/JetBrains.gitignore, +# plus misc.xml, which names the local Python interpreter, and sonarlint/, whose +# binary index files neither list covers. +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf +.idea/**/dataSources/ +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/misc.xml +.idea/sonarlint/ + # project build plan specific ignores -version.py +# 'co' is CMEM orchestration output, which is sometimes part of a build plan. co -*.xml -*.html *.bak artifacts .DS_Store diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index a722d73..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,101 +0,0 @@ ---- -default: - image: docker-registry.eccenca.com/eccenca-python:v3.13.13 - # all jobs can be interrupted in case a new commit is pushed - interruptible: true - before_script: - # make sure poetry creates virtual environment as .venv - - poetry config virtualenvs.in-project true - cache: - # cache the virtual environment based on the poetry lock file - key: - files: - - poetry.lock - paths: - - .venv - -stages: - - test - - build - - publish - -ruff: - stage: test - script: - - task check:ruff - artifacts: - when: always - reports: - junit: - - dist/junit-ruff.xml - -mypy: - stage: test - script: - - task check:mypy - artifacts: - when: always - reports: - junit: - - dist/junit-mypy.xml - -pytest: - stage: test - coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/' - script: - - task check:pytest - artifacts: - when: always - reports: - coverage_report: - coverage_format: cobertura - path: dist/coverage.xml - junit: - - dist/junit-pytest.xml - paths: - - dist/* - -deptry: - stage: test - script: - - task check:deptry - -trivy: - stage: test - variables: - TRIVY_NO_PROGRESS: "true" - TRIVY_CACHE_DIR: ".trivycache/" - TRIVY_DISABLE_VEX_NOTICE: "true" - script: - - task check:trivy - cache: - paths: - - .trivycache/ - -build: - stage: build - needs: - - mypy - - pytest - - trivy - - deptry - script: - - task build - artifacts: - when: always - paths: - - dist/*.tar.gz - - dist/*.whl - -pypi: - # publishing only available on a tag - stage: publish - needs: - - ruff - - build - allow_failure: true - when: manual - script: - - poetry config pypi-token.pypi $PYPI_TOKEN - - poetry publish - diff --git a/.tasks-plugin.yml b/.tasks-plugin.yml index a744d6f..3552437 100644 --- a/.tasks-plugin.yml +++ b/.tasks-plugin.yml @@ -12,7 +12,7 @@ tasks: - poetry run cmemc admin workspace python list-plugins uninstall: - desc: Unnstall plugin package in Corporate Memory + desc: Uninstall plugin package in Corporate Memory cmds: - task build - poetry run cmemc admin workspace python uninstall cmem-plugin-validation diff --git a/.trivyignore b/.trivyignore index 53933f2..5108c0e 100644 --- a/.trivyignore +++ b/.trivyignore @@ -1,4 +1,10 @@ # .trivyignore - -# ignore 51358 safety - dev dependency only -CVE-2022-39280 +# +# Vulnerabilities listed here are excluded from `task check:trivy`. +# +# Add one identifier per line (e.g. CVE-2022-39280), and always precede it with +# a comment stating which dependency is affected, why the finding is acceptable +# and, if possible, when the entry can be removed again. An unexplained +# suppression in a security scanner is worse than no suppression at all. +# +# See https://trivy.dev/latest/docs/configuration/filtering/ for the syntax. diff --git a/CHANGELOG.md b/CHANGELOG.md index aa3ff84..9243cb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](https://semver.org/) +## [Unreleased] + +### Changed + +- updated dependencies and template + ## [1.3.0] 2026-08-19 ### Changed diff --git a/LICENSE b/LICENSE index 36fde56..f0aeff2 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2021 CMEM + Copyright eccenca GmbH Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README-public.md b/README-public.md index ac9a102..363d8ae 100644 --- a/README-public.md +++ b/README-public.md @@ -4,7 +4,7 @@ Validate graph and data structures. [![eccenca Corporate Memory][cmem-shield]][cmem-link] -This is a plugin for [eccenca](https://eccenca.com) [Corporate Memory](https://documentation.eccenca.com). You can install it with the [cmemc](https://eccenca.com/go/cmemc) command line client like this: +This is a plugin for [eccenca Corporate Memory](https://documentation.eccenca.com). You can install it with the [cmemc](https://eccenca.com/go/cmemc) command line client like this: ``` cmemc admin workspace python install cmem-plugin-validation @@ -13,7 +13,7 @@ cmemc admin workspace python install cmem-plugin-validation [![poetry][poetry-shield]][poetry-link] [![ruff][ruff-shield]][ruff-link] [![mypy][mypy-shield]][mypy-link] [![copier][copier-shield]][copier] [cmem-link]: https://documentation.eccenca.com -[cmem-shield]: https://img.shields.io/endpoint?url=https://dev.documentation.eccenca.com/badge.json +[cmem-shield]: https://img.shields.io/endpoint?url=https://documentation.eccenca.com/latest/badge.json [poetry-link]: https://python-poetry.org/ [poetry-shield]: https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json [ruff-link]: https://docs.astral.sh/ruff/ diff --git a/README.md b/README.md index cfbd34b..81dc4ed 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,12 @@ Validate graph and data structures. - Run [task](https://taskfile.dev/) to see all major development tasks. - Use [pre-commit](https://pre-commit.com/) to avoid errors before commit. +- Agent instructions and skills for this project are in `.claude/` - your own + instructions belong in `CLAUDE.md`, which is never overwritten. - This repository was created with [this copier template](https://github.com/eccenca/cmem-plugin-template). [cmem-link]: https://documentation.eccenca.com -[cmem-shield]: https://img.shields.io/endpoint?url=https://dev.documentation.eccenca.com/badge.json +[cmem-shield]: https://img.shields.io/endpoint?url=https://documentation.eccenca.com/latest/badge.json [poetry-link]: https://python-poetry.org/ [poetry-shield]: https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json [ruff-link]: https://docs.astral.sh/ruff/ diff --git a/Taskfile.yaml b/Taskfile.yaml index 3682da5..715e490 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -1,7 +1,7 @@ # https://taskfile.dev # -# This is a generated file. We dot not suggest to edit it. -# Instead, create a file `TaskfileCustom.yml` and add your additions there. +# This is a generated file. We do not suggest to edit it. +# Instead, create a file `TaskfileCustom.yaml` and add your additions there. --- version: '3' @@ -64,6 +64,18 @@ tasks: - poetry run ruff format tests {{.PACKAGE}} - poetry run ruff check tests {{.PACKAGE}} --fix-only --unsafe-fixes + template:feedback-check: + desc: Look for findings worth reporting to the cmem-plugin-template + summary: | + Reads a Claude Code Stop hook payload on stdin and asks the agent to + consider reporting upstream when this working tree shows that a template + owned file got in the way. Prints nothing when there is nothing to say. + + Create an empty .claude/no-template-feedback file to switch it off. + silent: true + cmds: + - python3 .claude/hooks/template-feedback.py + clean: desc: Removes dist, *.pyc and some caches cmds: diff --git a/poetry.lock b/poetry.lock index 7581d29..f0ab81b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.3.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.4.1 and should not be changed by hand. [[package]] name = "annotated-types" @@ -6,7 +6,7 @@ version = "0.8.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.10" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "annotated_types-0.8.0-py3-none-any.whl", hash = "sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0"}, {file = "annotated_types-0.8.0.tar.gz", hash = "sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7"}, @@ -14,87 +14,88 @@ files = [ [[package]] name = "anyio" -version = "4.14.2" +version = "4.15.0" description = "High-level concurrency and networking framework on top of asyncio or Trio" optional = false python-versions = ">=3.10" -groups = ["main"] +groups = ["main", "dev"] files = [ - {file = "anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494"}, - {file = "anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f"}, + {file = "anyio-4.15.0-py3-none-any.whl", hash = "sha256:7ecd9937369ffce8bba0b5ccb9b3a9507b101b0ed50256aecfbab27e6c2acb99"}, + {file = "anyio-4.15.0.tar.gz", hash = "sha256:b5c620ed540725e2579c31b17bb995b3bf02c9281c9cace04c7d186380bab85e"}, ] [package.dependencies] idna = ">=2.8" +typing_extensions = {version = ">=4.16.0", markers = "python_version < \"3.15\""} [package.extras] trio = ["trio (>=0.32.0)"] [[package]] name = "ast-serialize" -version = "0.8.0" +version = "0.9.0" description = "Python bindings for mypy AST serialization" optional = false python-versions = ">=3.7" groups = ["dev"] files = [ - {file = "ast_serialize-0.8.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:3d822605fa7bb326ef868d25fafced7fc660fa46d9b90c02ea86d5e2f5d325f7"}, - {file = "ast_serialize-0.8.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:2efa40b068197d5efb62655b43baadb842ed71c4958cccd3e8b86a35726f0119"}, - {file = "ast_serialize-0.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:db1b957291bca08c7e72f43a12357b2948e20775d970e3fc3dac0aa3160ab725"}, - {file = "ast_serialize-0.8.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdc0d5b18ff8fb364e87923e47c0a91d0d69dbcaeaa274591f7fd26892cc3a3a"}, - {file = "ast_serialize-0.8.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9da7330f3e235bf7da89b8d39205c6350fc0c08a85379743f2df9fff87d6d980"}, - {file = "ast_serialize-0.8.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f3186969ee66a9863b00acc6523ace44c56974eecb348a7ea4b228d9f0b80e19"}, - {file = "ast_serialize-0.8.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40a57b73731be45da4fa41430c4d5dc94a24b3a4faba7b9e069978c0402064ea"}, - {file = "ast_serialize-0.8.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5075b9da3ef807eda752502446dfecea3b381c4900b7e27a5d5f4f899eb39951"}, - {file = "ast_serialize-0.8.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:293cc1c5bfa741f8e3fbe8175b9c07beee487c9a6fdbb25a5acad9f1df2d30a9"}, - {file = "ast_serialize-0.8.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0910c3442a75216dde0f102d854ba2aaa71d2482e0ee213630b9bf29584fba3"}, - {file = "ast_serialize-0.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:43dd6d596879bb1cb8a12cc9dae7bb10090a39a35883026c24f82488a195619a"}, - {file = "ast_serialize-0.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:8c9d537f59e936392cfd3597789d1390304dd659efc3c486ce7f40fb6b8a9f53"}, - {file = "ast_serialize-0.8.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:f0190a33d7f97c65e9069f7a7f40499eea6b5cbe260c558378109caf20ce934b"}, - {file = "ast_serialize-0.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:77308ae6c5cf5264cc0f01a7c556ec77a9e68eb1f61b093534d698139fdc3b14"}, - {file = "ast_serialize-0.8.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8d53a23f27e1ed3a36b2d26fd2a1a6228c8e85a1ed62ff7cdb44bd610769f20a"}, - {file = "ast_serialize-0.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ffa5e7cb08f96fed9121f77b224151e41caf88feab9d652bb46c78202b6fbeda"}, - {file = "ast_serialize-0.8.0-cp314-cp314t-win32.whl", hash = "sha256:fa70ed4dea0bb18b30a1789c77baa701d0ef30c474f2ccabdea61e25623a8827"}, - {file = "ast_serialize-0.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d8b3c8eee4c1baef9d4e84d2a59a805501617127be42615cb48970b15b0892b6"}, - {file = "ast_serialize-0.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:ac4f0a83c55a9b782f79ad55a5247b7db123c1db405959791c2ef886e9710c9f"}, - {file = "ast_serialize-0.8.0-cp315-abi3.abi3t-macosx_10_12_x86_64.whl", hash = "sha256:86b8a1e6d90467345356098b040150e82fbc26d24a7a202224b13dc1f6264ca0"}, - {file = "ast_serialize-0.8.0-cp315-abi3.abi3t-macosx_11_0_arm64.whl", hash = "sha256:39e92ff8e8cb45947fe9007174b2950e1fb098e6abd00266a13cd3bcf6675068"}, - {file = "ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c85d8d18db5b2dfcb3b7e38a4d600ca35504c0ed8a6f75cd1c811e4ffe248a15"}, - {file = "ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9830ff7e764f74d9eefb01170c61a9f0fd2c027dac5fcb72e064decd57d56371"}, - {file = "ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6479d9722a4cd21b578f5478074c41e6169f04811996ec881655560f703a5bba"}, - {file = "ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a63bed264e818cd83eec11feed0f50aa162542b91132ef58afebc857182763a5"}, - {file = "ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d187197d234aa45d6cfa2b096be5f666e8cc2e7eb3722d0ab8926293cf5720c"}, - {file = "ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_31_riscv64.whl", hash = "sha256:2d39a56282cfcc0d8eeea37267c754be59c98d48505c23b1dae5c6011f3813dd"}, - {file = "ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f7cc5f10386994c0f4844f1e6d6a97127e9b478660eb6dec2b257644f0acab64"}, - {file = "ast_serialize-0.8.0-cp315-abi3.abi3t-musllinux_1_2_aarch64.whl", hash = "sha256:6102f2f985c2e542be85cd857678ec9356fefa792b93cadfadd31139f5696f27"}, - {file = "ast_serialize-0.8.0-cp315-abi3.abi3t-musllinux_1_2_armv7l.whl", hash = "sha256:3a8660fe66667b76a6e9dccd1d33e66b229fde3b308db991c041609226c005b6"}, - {file = "ast_serialize-0.8.0-cp315-abi3.abi3t-musllinux_1_2_i686.whl", hash = "sha256:e7266307e5fba39836edb79def8608887af48820508bff3c5f2941e1e04d1534"}, - {file = "ast_serialize-0.8.0-cp315-abi3.abi3t-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca7e6fd1ad845d1cc649dc2ecd499db2f8f46af5bf8da7b70dd858774cc038b"}, - {file = "ast_serialize-0.8.0-cp315-abi3.abi3t-musllinux_1_2_riscv64.whl", hash = "sha256:2880350b13d3eae69a0d70bc1fb6c9bfaca4dbd0e20ba8cd1aa483080b56ff06"}, - {file = "ast_serialize-0.8.0-cp315-abi3.abi3t-musllinux_1_2_x86_64.whl", hash = "sha256:ab0f9a59f7d63d0d441b56b9a818b273705264352d5115cfee12e940e816d958"}, - {file = "ast_serialize-0.8.0-cp315-abi3.abi3t-win32.whl", hash = "sha256:0485a25ef519c62e749ee3c1ad8070e591b380d67226349eb5a70b228dc1ac4a"}, - {file = "ast_serialize-0.8.0-cp315-abi3.abi3t-win_amd64.whl", hash = "sha256:bd84d60bca7079e741be4ac5dbe237751a59d7f6f9f0126b11880d63822cbe16"}, - {file = "ast_serialize-0.8.0-cp315-abi3.abi3t-win_arm64.whl", hash = "sha256:057769b5921336eb2d9124f2a731b42ed05ffdac559b840dbdf6f3937cf153dc"}, - {file = "ast_serialize-0.8.0-cp315-cp315-pyemscripten_2026_5_wasm32.whl", hash = "sha256:a02cbed7d8bfdcdee88edaac12bd50d53d9953aaa2e1852ef078625be5f1c0b5"}, - {file = "ast_serialize-0.8.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e1bd223df0f6c96b396975fa604cb33bce53d9b4a0185490be4c4a289f7c9c87"}, - {file = "ast_serialize-0.8.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ddd3b61f45c132da66c5476b281891e08c1fd87fbdabe8a6973e1622efc85f06"}, - {file = "ast_serialize-0.8.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f9caa63fad8241257ae401b5ff0a64026c6adb36b8e86cbe8782d9ea505daf6"}, - {file = "ast_serialize-0.8.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3926fa117b5e65019853a2969966d11c7175af377a3425991f3fe73784412405"}, - {file = "ast_serialize-0.8.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:485f1113af805e9e170b95ef993ca3fbd4f89c04bab25c58b4fc632d854801ab"}, - {file = "ast_serialize-0.8.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ccebbed24f1281062d5852353c72c47502955926cfcb8345ffb3a44d87ff3d3"}, - {file = "ast_serialize-0.8.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:252f883290d1cdb728eb7fe1d9a7221b88af5a329aae0bc91ddee4dafb820331"}, - {file = "ast_serialize-0.8.0-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:96abc072ad29db8d02194afd47d68987322622787daceae82398d7b69f3ba2e6"}, - {file = "ast_serialize-0.8.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9118ad3e369727060b2696fc4078f250ecffca4248ba87f537f55cea9f9dce06"}, - {file = "ast_serialize-0.8.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f359df4bd921918af8bebd142a376c77511d7151cc8ba852760b587b5a4a54f3"}, - {file = "ast_serialize-0.8.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:e94f9121d13fa36cbf21314783c77d05ae3a0868decd18cf5233fdcc6de49ac8"}, - {file = "ast_serialize-0.8.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:54f95b486018d262bcb387a9afd96f0da74508b442762b80c769454a6fbb3ee3"}, - {file = "ast_serialize-0.8.0-cp39-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:4c38b915511e32bc718c49dbce98ff9af36bac0ad6a604f58000cd5e3aecdba7"}, - {file = "ast_serialize-0.8.0-cp39-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:9a2ef9cf12f2de4f1028c42c1dd7d775255e0fb3e5bb48896c97e35ef52366fe"}, - {file = "ast_serialize-0.8.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6f18048fe9f6dd266bd577cdec48bdcecb74faaa01fe941324435483b013ed2a"}, - {file = "ast_serialize-0.8.0-cp39-abi3-win32.whl", hash = "sha256:31883542dd6c94d178f5db3d32fbd69c5eb88b3a7c018e7ac8cc0c45195ddbed"}, - {file = "ast_serialize-0.8.0-cp39-abi3-win_amd64.whl", hash = "sha256:861794565b06337005c1447ef23103a3d5a627d08bdc827870d00d0b28ef5f51"}, - {file = "ast_serialize-0.8.0-cp39-abi3-win_arm64.whl", hash = "sha256:b2a5978662fd4db463dfb4b974d2b10ac6430b98f5333aabc7051909df3561d0"}, - {file = "ast_serialize-0.8.0.tar.gz", hash = "sha256:6c37c43e4004dfb42d321ddedc569dc17ff4259296f3af577c9ea46a809bc010"}, + {file = "ast_serialize-0.9.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:ae1c46eb97865823f9843c4b80145e011874923e1a4a44b45738a5309d83e9f5"}, + {file = "ast_serialize-0.9.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af082cb7e6c4fa3a428aa616c13d709a944076eba84a73184de15621cc1a915d"}, + {file = "ast_serialize-0.9.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7e9f2540741ad10657a209209f7e5cc6b530eb3ed145fd77258ab43542d96ad7"}, + {file = "ast_serialize-0.9.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95485d5e8704af2ecc7f723757b88f992ae8122028d687ccf877cad2b4c3da4"}, + {file = "ast_serialize-0.9.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b559cffac5a71a698d9194e4295765ff2132a10fd1284860f02b30f12c1f729e"}, + {file = "ast_serialize-0.9.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dad8a3f7106efcf252fc289c092ee0cee5c3512c0088bcf3fffa01458323092f"}, + {file = "ast_serialize-0.9.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:24de2bc930b7e1ca86641136b9875a1e5f80f52b484deddc67893eeaf9077bd9"}, + {file = "ast_serialize-0.9.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ac7b4cdf89ca8318aae157d824017596784982851c8a89a621973f261574696"}, + {file = "ast_serialize-0.9.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:95dcb93f30258dcf09d9b6a302dac9323b70e9df8c18ee0bf4ea1fa7cc5f1875"}, + {file = "ast_serialize-0.9.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1ed5824b4b2fa37ad93fa2db23d8243a3d315b3e2d7ca70f99b2727d8788af05"}, + {file = "ast_serialize-0.9.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e3c69f2ce565c786bd3853ce42495e8a63610516f79651c7cb4f2cd0ddfaee52"}, + {file = "ast_serialize-0.9.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:f8c824d822a2ac54ec4228b88c0d170ab0024cf285eeee57b9d4f994003fb553"}, + {file = "ast_serialize-0.9.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:c55010ea0fafc6bc8231809d328bda781bfe41a01c43522be5eb3713fd855cda"}, + {file = "ast_serialize-0.9.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:322282ac5337e5e776bc416f2b5201e680fa4dacf92ea739bd35e46a28a66c41"}, + {file = "ast_serialize-0.9.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:ded5ed06ec469407d6cd571ace7a7a25809cc388e4bac1dd35c7747469fc7fdf"}, + {file = "ast_serialize-0.9.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3ee40752ddb4fb5c6a67161d13f3ce3df7987dcb9272260542a86b0be1519ae2"}, + {file = "ast_serialize-0.9.0-cp314-cp314t-win32.whl", hash = "sha256:d9c635eacfc02b91da6796d3b5ff9086e511b8a29b19f9b3f4f978b8d170f838"}, + {file = "ast_serialize-0.9.0-cp314-cp314t-win_amd64.whl", hash = "sha256:62a96e327e2a178d6c295b10422e95c992a9286f0ec1b2bc7cd5b4873252a38c"}, + {file = "ast_serialize-0.9.0-cp314-cp314t-win_arm64.whl", hash = "sha256:41da4332492222d56345d5e436eed4fbec76caadee959f6ffa3cd2fc1bd51895"}, + {file = "ast_serialize-0.9.0-cp315-abi3.abi3t-macosx_10_12_x86_64.whl", hash = "sha256:383f56e3ae925f154632458f01b4bfcde3dd382f3ec04f5c7f6d72f76524ff48"}, + {file = "ast_serialize-0.9.0-cp315-abi3.abi3t-macosx_11_0_arm64.whl", hash = "sha256:b9ef3d4173907bd19aa8f1683be9f06e7862e6cdf2ca6bca3633305c0df32063"}, + {file = "ast_serialize-0.9.0-cp315-abi3.abi3t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9482672ca8ec09f85cd050a053fb88c30c882c8e20ce7a140d8defe19c0ef2eb"}, + {file = "ast_serialize-0.9.0-cp315-abi3.abi3t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6a568d1d489f0669a31aed90ca4845aa7f08e1b8cd5d05e1905dbdc3ae9b2b0"}, + {file = "ast_serialize-0.9.0-cp315-abi3.abi3t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a4dd005d4095a13eb312dc712c943c7730f262b000a87df963925328a38ffdb"}, + {file = "ast_serialize-0.9.0-cp315-abi3.abi3t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:207ac73afa1f4654840593853c130eac2591dd942434176eea33f730afb3359b"}, + {file = "ast_serialize-0.9.0-cp315-abi3.abi3t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ae01129e2cc5d57a3c434d8a990019de039350310a2e1dd3c9f61311964cf25"}, + {file = "ast_serialize-0.9.0-cp315-abi3.abi3t-manylinux_2_31_riscv64.whl", hash = "sha256:4c522377f383670abfe21c94edc3032cb3bd34d8fcacd280fa9556907d4edd4b"}, + {file = "ast_serialize-0.9.0-cp315-abi3.abi3t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4cd886f6e900f5e13f758cb8ef359652e690e4f3f9c257a7269e095940534167"}, + {file = "ast_serialize-0.9.0-cp315-abi3.abi3t-musllinux_1_2_aarch64.whl", hash = "sha256:f3f0f3359cf0f22bf096b07021ffe6bf0ec88ac8a2cf7ce5f4701af973112faa"}, + {file = "ast_serialize-0.9.0-cp315-abi3.abi3t-musllinux_1_2_armv7l.whl", hash = "sha256:6c428444656cffbd32c1e76626c6eec5237b58c8fcb0b5d3df75941cd50c4f3c"}, + {file = "ast_serialize-0.9.0-cp315-abi3.abi3t-musllinux_1_2_i686.whl", hash = "sha256:54f0babed5e2a4eb86a0716ac612aff33f933e7572e5bc067adcdbe672a26321"}, + {file = "ast_serialize-0.9.0-cp315-abi3.abi3t-musllinux_1_2_ppc64le.whl", hash = "sha256:1b779fdaee34d19900a5ba5fd6bd4cefe225650081f9de28de256eacee5113c2"}, + {file = "ast_serialize-0.9.0-cp315-abi3.abi3t-musllinux_1_2_riscv64.whl", hash = "sha256:4db7f524eaa857fbe650cac33b9cedb5ccda14393d40f640b75dfb06aa13c98c"}, + {file = "ast_serialize-0.9.0-cp315-abi3.abi3t-musllinux_1_2_x86_64.whl", hash = "sha256:d2a37795a90809da6094825e7063118b4cf723b8701b134973b4566ed8b9ea09"}, + {file = "ast_serialize-0.9.0-cp315-abi3.abi3t-win32.whl", hash = "sha256:4411d1cba9eeecb301365343a7e96813b4a44fcdb20181557867ff7e751804cf"}, + {file = "ast_serialize-0.9.0-cp315-abi3.abi3t-win_amd64.whl", hash = "sha256:b5c3724faf780e25def89c369eb6340a15dff06ac348160c61781e2373a4cd10"}, + {file = "ast_serialize-0.9.0-cp315-abi3.abi3t-win_arm64.whl", hash = "sha256:1de0933a4c1d104d77d6e75f053f5e628b54cf8f9fea809b8250cb04cda07bd3"}, + {file = "ast_serialize-0.9.0-cp315-cp315-pyemscripten_2026_5_wasm32.whl", hash = "sha256:5fc57f17fb4ce49b4eeccfcd2670e4a55659bd740bb8e8aedbe511ccab8b5f03"}, + {file = "ast_serialize-0.9.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:dac690f99538d9df0d23ce0299e946add2744b007a36b480a292fe361c82553d"}, + {file = "ast_serialize-0.9.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:2223ead73b5a5399d39610cf9c4164ad0b2bf2025226626b87ae15226d93d3f7"}, + {file = "ast_serialize-0.9.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b7c5f5838408fb000d76abd14e886836412b7ec7eccd028dbb5ed5819780008"}, + {file = "ast_serialize-0.9.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1e05701fde79affa1cc53e391867f9da3eb03fa8501f87354292796b0f8398fd"}, + {file = "ast_serialize-0.9.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d013c36eb2f2ac0cb7d4d0e79918a92ab00fbce8f1542fe47f34a46e06168f82"}, + {file = "ast_serialize-0.9.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19cde5c2110f7b90ab1210a599178524f6c9f34862b20ba2b9aa7832c67bb35d"}, + {file = "ast_serialize-0.9.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1514f4a39704e2e815f9fc675fc13f19f694f212086b520110840782cf3c5295"}, + {file = "ast_serialize-0.9.0-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:30651ccdec6d23c49ee4711b1a1096d8dbd3be38eecf2f09fdd98a608ce7ac24"}, + {file = "ast_serialize-0.9.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d9a46caf5e3f2cd266e8638b2f4ea8bf54cf376f015f8418397b4633fdb38e9b"}, + {file = "ast_serialize-0.9.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:aed6e413c6c22a23c33c47a01dd2adce01d7a7ed408748e896903f47d0a1aa47"}, + {file = "ast_serialize-0.9.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:871fb7c5b049897ee137b67efad7fe4545ad270f7eccd970da877833f8e63aa7"}, + {file = "ast_serialize-0.9.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:bb378efb5537b43f38660e2e6d6e138a40885cf191d43443bb3ff7ff47e9cd9b"}, + {file = "ast_serialize-0.9.0-cp39-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:b373beff65b01fcffca5aaad3269ae629f3a998b09efdd3635e48039008a5dec"}, + {file = "ast_serialize-0.9.0-cp39-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:25c8d517c45cf2b1820fc2af6ac593783654818f79d05646d25d624360678a4e"}, + {file = "ast_serialize-0.9.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1675dc46578298ae00936164a160997801a6ca2385913150d8d16df634296cf3"}, + {file = "ast_serialize-0.9.0-cp39-abi3-win32.whl", hash = "sha256:20fce3885eeff05a3d6afefa845c8168016e3ea1f6fc9cdc84c8db28b863a550"}, + {file = "ast_serialize-0.9.0-cp39-abi3-win_amd64.whl", hash = "sha256:161914666a21d48b681982146ac0fa4086ef099d91c637cf595387f5f06aa099"}, + {file = "ast_serialize-0.9.0-cp39-abi3-win_arm64.whl", hash = "sha256:74473258a5c55855d5306c864a5c799fbff03a0f0ea1197346b2b5cc5b4ea48a"}, + {file = "ast_serialize-0.9.0.tar.gz", hash = "sha256:79fe8be1c934aa572940d1811d8dbe4d1b6f22291e3f16755c9b062e9ac92fb7"}, ] [[package]] @@ -138,7 +139,7 @@ version = "5.0" description = "Define boolean algebras, create and parse boolean expressions and create custom boolean DSL." optional = false python-versions = "*" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "boolean_py-5.0-py3-none-any.whl", hash = "sha256:ef28a70bd43115208441b53a045d1549e2f0ec6e3d08a9d142cbc41c1938e8d9"}, {file = "boolean_py-5.0.tar.gz", hash = "sha256:60cbc4bad079753721d32649545505362c754e121570ada4658b852a3a318d95"}, @@ -346,19 +347,16 @@ files = [ [[package]] name = "click" -version = "8.4.2" +version = "8.5.0" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "click-8.4.2-py3-none-any.whl", hash = "sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76"}, - {file = "click-8.4.2.tar.gz", hash = "sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6"}, + {file = "click-8.5.0-py3-none-any.whl", hash = "sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360"}, + {file = "click-8.5.0.tar.gz", hash = "sha256:ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34"}, ] -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - [[package]] name = "click-didyoumean" version = "0.3.1" @@ -394,14 +392,14 @@ dev = ["mypy", "pytest"] [[package]] name = "cmem-client" -version = "1.0.0" +version = "1.1.0" description = "Next generation eccenca Corporate Memory client library." optional = false python-versions = "<4,>=3.13" -groups = ["main"] +groups = ["main", "dev"] files = [ - {file = "cmem_client-1.0.0-py3-none-any.whl", hash = "sha256:b745a971669f7d6cbf47c1fbfc9fce11bec9f572065202a0ef593d4634e97968"}, - {file = "cmem_client-1.0.0.tar.gz", hash = "sha256:a0a1c2e893cd30df9f234568ac2f8c63fb923cf97aef750bcff2505e736b027c"}, + {file = "cmem_client-1.1.0-py3-none-any.whl", hash = "sha256:b67025e96f7d1b4a861d401f76e19deb990cb9c70b684ccb776067aaad7b2833"}, + {file = "cmem_client-1.1.0.tar.gz", hash = "sha256:5545e4718321e537b3945372790772a532df0d4cd67acfc6d28d6f529cd4d32b"}, ] [package.dependencies] @@ -418,58 +416,57 @@ xdg-base-dirs = ">=6.0.2,<7.0.0" [[package]] name = "cmem-cmemc" -version = "25.6.2" +version = "26.2.1" description = "Command line client for eccenca Corporate Memory" optional = false -python-versions = "<4.0,>=3.10" +python-versions = "<4,>=3.13" groups = ["dev"] files = [ - {file = "cmem_cmemc-25.6.2-py3-none-any.whl", hash = "sha256:e411a9677ccd384aec609bb7e0dadc8a9805bda5ad19c37d000531bf46e9beab"}, - {file = "cmem_cmemc-25.6.2.tar.gz", hash = "sha256:d1e55a0ddf338603d76d1e87660e6e0767b550d7814e70f06fe3771c3603b2fc"}, + {file = "cmem_cmemc-26.2.1-py3-none-any.whl", hash = "sha256:2214372210783fe8f3617b4f63b8d71952109d00d30e3d9f6c46184dcfc4cbbd"}, + {file = "cmem_cmemc-26.2.1.tar.gz", hash = "sha256:25d0c37e2b120c6f94c63763118802a244fde5ae16fed3c3f0ade3a1dd53f0ca"}, ] [package.dependencies] -beautifulsoup4 = ">=4.13.3,<5.0.0" -certifi = ">=2024.2.2" -click = ">=8.3.0,<9.0.0" +beautifulsoup4 = ">=4.14.3,<5.0.0" +certifi = ">=2026.1.4" +click = ">=8.3.1,<9.0.0" click-didyoumean = ">=0.3.1,<0.4.0" click-help-colors = ">=0.9.4,<0.10.0" -cmem-cmempy = "25.4.0" +cmem-client = ">=1.1.0,<2.0.0" configparser = ">=7.2.0,<8.0.0" -humanize = ">=4.14.0,<5.0.0" +humanize = ">=4.15.0,<5.0.0" jinja2 = ">=3.1.6,<4.0.0" junit-xml = ">=1.9,<2.0" natsort = ">=8.4.0,<9.0.0" -packaging = ">=24.2,<25.0" -prometheus-client = ">=0.21.1,<0.22.0" -pygments = ">=2.19.1,<3.0.0" -pyjwt = ">=2.10.1,<3.0.0" +packaging = ">=26.0,<27.0" +prometheus-client = ">=0.24.1,<0.25.0" +pygments = ">=2.19.2,<3.0.0" +pyjwt = ">=2.11.0,<3.0.0" python-dateutil = ">=2.9.0.post0,<3.0.0" -requests = ">=2.32.3,<3.0.0" -rich = ">=14.0.0,<15.0.0" +requests = ">=2.32.5,<3.0.0" +rich = ">=14.3.2,<15.0.0" six = ">=1.17.0,<2.0.0" -smart-open = ">=7.1.0,<8.0.0" -timeago = ">=1.0.16,<2.0.0" -treelib = ">=1.7.1,<2.0.0" +smart-open = ">=7.5.0,<8.0.0" +treelib = ">=1.8.0,<2.0.0" urllib3 = ">=2.6.3,<3.0.0" [[package]] name = "cmem-cmempy" -version = "25.4.0" +version = "25.5.0" description = "API for eccenca Corporate Memory" optional = false -python-versions = "<4.0,>=3.9" -groups = ["main", "dev"] +python-versions = "<4.0,>=3.10" +groups = ["main"] files = [ - {file = "cmem_cmempy-25.4.0-py3-none-any.whl", hash = "sha256:ae68e978ad3c3b6e7713760ba89ddd9825fe1cf1f668a78e79f0ce5bccecda9c"}, - {file = "cmem_cmempy-25.4.0.tar.gz", hash = "sha256:c1fedf566c1784a6142d319a2580e0a9a8d2255144bb12237ac3761b3842ccaa"}, + {file = "cmem_cmempy-25.5.0-py3-none-any.whl", hash = "sha256:b2f7e766a98c16a49c354a29e67a334c644c77cc659efbaa764579449ceaf8ea"}, + {file = "cmem_cmempy-25.5.0.tar.gz", hash = "sha256:1260b5abe84ba846f317648bd1dc1b88ba4927286e4eafc4c5e7fe0ca04ed23d"}, ] [package.dependencies] certifi = ">=2023.7.22" pyparsing = ">=3.2.3,<4.0.0" rdflib = ">=7.1.4,<8.0.0" -requests = ">=2.32.4,<3.0.0" +requests = ">=2.33.1,<3.0.0" requests-toolbelt = ">=1.0.0,<2.0.0" [[package]] @@ -497,7 +494,7 @@ description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" groups = ["dev"] -markers = "platform_system == \"Windows\" or sys_platform == \"win32\"" +markers = "sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -525,133 +522,133 @@ type = ["pytest-mypy"] [[package]] name = "coverage" -version = "7.15.4" +version = "7.16.0" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "coverage-7.15.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d0be6daac4cce6b8c8dc65886bae1b082ddbca4da8e5cbb5e15166acf253e264"}, - {file = "coverage-7.15.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b24e078eabcd6a9caa8b0713f9bc1eeb310bcc960a29d45a3b4fcd4b16d5b11d"}, - {file = "coverage-7.15.4-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cfe20cc8cf8821d4fe54f89106cbf06aa27f37b5bbe3535568065a81539b4150"}, - {file = "coverage-7.15.4-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:83cf06cdd687677742caff1a9134833b7a8b75f111519d2cb0e0ba1b9a851e15"}, - {file = "coverage-7.15.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8fa4de68e2a752468ff14b4e15db7def689a71be759e826a31ccecbef69c5fd0"}, - {file = "coverage-7.15.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4dff9daa47d83120c3ec38ce921214242944a832aa04e903e50b5b7ebac8972d"}, - {file = "coverage-7.15.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a093fd37229918976f602aa07aa59e0973cde82186f220c8e197f721f5be0ce4"}, - {file = "coverage-7.15.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:317db01a2cb02552fd67e2b1cca77a4b528a2a277176c5e0bf2cecbb639d3f54"}, - {file = "coverage-7.15.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:8ee3838dcb656602c3b51e16aed9bfb0822f8d8d6d1c5966d32ec8c104be8e20"}, - {file = "coverage-7.15.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:425920379052ff1fe465268f3361d35804a241bbdd5a1b592c8cb60df4c52325"}, - {file = "coverage-7.15.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:69bb2400abef928e365ea7d4d9925169ada78ed2295546780002d4b65de3df88"}, - {file = "coverage-7.15.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:81661f82d302484e3119e7c80c519c02fa9bcc2a6b339baf67d67bc89c580f04"}, - {file = "coverage-7.15.4-cp310-cp310-win32.whl", hash = "sha256:cb476b2e828ecb71cb6b6a928d23fd20a7ddb501188022dae1c37499149cc338"}, - {file = "coverage-7.15.4-cp310-cp310-win_amd64.whl", hash = "sha256:3fc2130bf37df31852a8384f12601563a45a0024bccc6624f38355cba7a8d360"}, - {file = "coverage-7.15.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bbac5abad70df71019988f83f26ac7092ff2642975def4429e98dc7585ef3490"}, - {file = "coverage-7.15.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:357a173465c7ce028d07a95cc2b63b5bf59f50ecdd5ad75c5cbb78ada984048e"}, - {file = "coverage-7.15.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:21b803935e2efc3acebe9697197a294fccf5dc4e5382bd6369542ff7a7d2a1d7"}, - {file = "coverage-7.15.4-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7a2b580774a4786c1053157c0165e04476e03ff293993d7c148eee784a94bae6"}, - {file = "coverage-7.15.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a9464451c4efffe8d47ace5a540b10b0dc10e879066290f8600872b7f54a419d"}, - {file = "coverage-7.15.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:de602f34123c2f4af1c1869c6dbbbd60da6d5983bf01937367295d135cccbfce"}, - {file = "coverage-7.15.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6879ded16a27f3eeca19b900c147e81616e7054db451471a611b2755ee5249f7"}, - {file = "coverage-7.15.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:986be58c3ab54aae8d3496a6225eea74f760fdbe739b38bd442c7e8d133aa53b"}, - {file = "coverage-7.15.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c6103639613fe6c1e989082948419bc77a2d26b6c825c99d7fad25f7d3d87afc"}, - {file = "coverage-7.15.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d3af93dddb5659276c63bc16ac6466ac2033a70ca816097bbc06345b8ccdf571"}, - {file = "coverage-7.15.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:b10075e5421d04265766a6d1dac809bbeb8a946fbb23c8f82c227409b2190719"}, - {file = "coverage-7.15.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a67a9f78b2942d87ba8ce3059c642164d2aedd65337377fb52fe9803656bc5c7"}, - {file = "coverage-7.15.4-cp311-cp311-win32.whl", hash = "sha256:69484d1aca26e322e1c3ce03f09341e84524ababad2d7202161738d83cc9f82e"}, - {file = "coverage-7.15.4-cp311-cp311-win_amd64.whl", hash = "sha256:63fd6fcd1dd6e158f7eb78606e72933b3f6d01e7b747f99c6c12d764307a0fdc"}, - {file = "coverage-7.15.4-cp311-cp311-win_arm64.whl", hash = "sha256:ea82116c9893fa89e929b7f197ee5a1950a76e91cc5c85ba503fc02379d04890"}, - {file = "coverage-7.15.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d4fedd1f7f428f9fe83b1ead5e7cc87a43427be31aadafbac3ac0636dc7abb22"}, - {file = "coverage-7.15.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:37e2f0cdf58e2e1fed4e4d5a8f8786ae2f7eb80b478016876667dc4a01d60a97"}, - {file = "coverage-7.15.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fb55d0e70bb15f2e81477613627286581414693d74ac7963c93a790dd453ca9d"}, - {file = "coverage-7.15.4-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:899b9da30f3c6c336566e3707495bb23e8302d39d862f01fa78c48b99b9437e2"}, - {file = "coverage-7.15.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d15715e8c46552827e5e4f30a35575a2dbcad14454cf3284c54483946bd16931"}, - {file = "coverage-7.15.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:002a438859f7b430bc99afeaf01a6d187dad1d0dc907b64cdeffc632a5db8fd8"}, - {file = "coverage-7.15.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e4193a04b518f7968f3099755f5509ee7cccc6dc2b92a6b14841934d22e222c9"}, - {file = "coverage-7.15.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e98dcc55d572b38e69d117da7e8e8efb8500f1f5eaf81ecd460a63220790b839"}, - {file = "coverage-7.15.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:af6c538498ce66c10d3fd541c2a8d5b03da5850355add34e6cba564210cb9e72"}, - {file = "coverage-7.15.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1d10025d96ea89fc2f73714dbc4cbd433fe012c1ac9e23f895d7728b238b6e52"}, - {file = "coverage-7.15.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d802e1947603162ded419bff83ac7489820355d2b856dfb09206574e3a37ac0c"}, - {file = "coverage-7.15.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c2de40895718f91951b86712b4c5b694acaf9a0a49be13874896f599a1eed3f4"}, - {file = "coverage-7.15.4-cp312-cp312-win32.whl", hash = "sha256:5c3431b2161279b7db5c2a1aa58ae02e5cb8c3c42d93a5094be3f5537bd5b11b"}, - {file = "coverage-7.15.4-cp312-cp312-win_amd64.whl", hash = "sha256:6befeab5fb2b51c958ca4ac6c5d141a1e8240f4f76e46350f1911963deda49cd"}, - {file = "coverage-7.15.4-cp312-cp312-win_arm64.whl", hash = "sha256:67bc345491ab55b837277d76f5775d057e8c7f1ac44d890d8c2c82adde258c6f"}, - {file = "coverage-7.15.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c705b28feb2775dc82a25f1d473a370bc37ff93f5177f4e29ce2425f560f6921"}, - {file = "coverage-7.15.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3ff205ab5e3ecc670f6a4dd19d9cbf12ede53dd41cfc1e15716ec961ea6d314e"}, - {file = "coverage-7.15.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5172326e861a38b48b48befca15e0f477a26b283337a33a739c8fed229934e36"}, - {file = "coverage-7.15.4-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:12b59c90084e3234fb11184886bf4a40f4f16a8c8f867be2e087b81f8e8868d4"}, - {file = "coverage-7.15.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:349062d66f00b40fa2c1c222438bad25fabf755631b5d82937fe985c8008615c"}, - {file = "coverage-7.15.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4256ced708e598e05209bc1a8ab4074e04a51dba4c62fb45926a229af675ace7"}, - {file = "coverage-7.15.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d80f974b20782d9612c8b4c9beeca867074c7cf4079d1419843fa25a26428b25"}, - {file = "coverage-7.15.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2e179f19bfe1d31f8eeeaa12990194d761c4f62f0759661000bca6cd8729f40b"}, - {file = "coverage-7.15.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8bc16bb47b7679670eceff71d78bfb7d6e5b143f6c2cd117487ec7c75e0d4b78"}, - {file = "coverage-7.15.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1cd685005cd2c4200adfc14cf39a603b9320efab3f18a8f7f156d20c9cc3345f"}, - {file = "coverage-7.15.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:337399ad2c93b3acd2a937627dae8b3e86b66707cd3d3e856347999aadf1ef8d"}, - {file = "coverage-7.15.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:96e257121228ec5cd2bb919276e94ac11074471bc37d68dbae0e8308cce15fff"}, - {file = "coverage-7.15.4-cp313-cp313-win32.whl", hash = "sha256:c65a9e0dfc6143491879da4e13b5e30f8be192055de508d737fb14601edbd22c"}, - {file = "coverage-7.15.4-cp313-cp313-win_amd64.whl", hash = "sha256:2ff8f5e9b8f7a94f0c11c45631eee103dbcb7d63274edd12c56efe1be690b3b4"}, - {file = "coverage-7.15.4-cp313-cp313-win_arm64.whl", hash = "sha256:6e0a8a5083b096487d6cfced94cdd514d8f5db6f113610fb36c0620edb1028cf"}, - {file = "coverage-7.15.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:770e9325ab5ea6d56f77e59b29ecfe0ac20b57a82a601876f90494a4dda0386f"}, - {file = "coverage-7.15.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d12b33a3a50a1676b7784dc8d00a0c6d66a9f2add4b85a041c19b6a7e53ef23c"}, - {file = "coverage-7.15.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5669c8378ebde86f5def7a25d29586631b58acc27ffde04399f678f3dfc6e082"}, - {file = "coverage-7.15.4-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ff97a14362eef486483ed44042ca2027ea257df6ff768e62358ee0c9776925ac"}, - {file = "coverage-7.15.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a325e815318638aed1655d9c06e6d7c2d3d46c09231ce988070428a8762d734"}, - {file = "coverage-7.15.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:474223409d88eb20d2d6a0d37ea60e8647a65a90cc008dc1f0410af5f64f1e0d"}, - {file = "coverage-7.15.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7f2f62ae3cd189dd2e13aece758c57b3eecbd27be070dbd4cbd10936049e5dbf"}, - {file = "coverage-7.15.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:39ece820e29e0a2ba34b3ecb3be83c27e997eed8926f2ba6fe7ce7a0bda5843b"}, - {file = "coverage-7.15.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f21b56dcace11dfe013014201f577dcd592b2a9b72182d930361b47cf6f73f25"}, - {file = "coverage-7.15.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:93a3a0b662abcc10c73a47cbc72cd60f63618d6989fb2d1286e50eacd974f303"}, - {file = "coverage-7.15.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:141fae2cabf5569b782c10afc4c850ce10f618c13f8db54765cba99cc839da1f"}, - {file = "coverage-7.15.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:81294c7e6ab30c5f74c0353b11b2fd6320e72d9bee6ac73b357caa8b916323a5"}, - {file = "coverage-7.15.4-cp314-cp314-win32.whl", hash = "sha256:7bbd7d6418e0dab31a206af5203bd43ae36edb8e7fba1940b055d3e9249290d7"}, - {file = "coverage-7.15.4-cp314-cp314-win_amd64.whl", hash = "sha256:f0204ed122758782970526057093f448051a39db9d810d4e344bb87a3546f425"}, - {file = "coverage-7.15.4-cp314-cp314-win_arm64.whl", hash = "sha256:9e71e7bc71c686a123347ae47a0de33a175e797a85bb57b791492adf4eec8ed8"}, - {file = "coverage-7.15.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7c922735321eef3f87c280a3d39afff6b646723a2880b862cda4ac7a093b8aa8"}, - {file = "coverage-7.15.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f41c17c4668a655ce96d090d8d5ffdc24ef64b5a02f9753884d08483e8a4a41a"}, - {file = "coverage-7.15.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:46822e9b6ff1c6a72b518c162c44a8f45a61a1d609c51084bf5b16c023c5037b"}, - {file = "coverage-7.15.4-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3d6f4955b73b5445271379a59e3792b0d978f42d4a01e0cf7a67d9c33a3bb0a5"}, - {file = "coverage-7.15.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3fc9e047706fb4a9abb54f719d3aa643e80e5bb3818182c40aee01ac0f0247ba"}, - {file = "coverage-7.15.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05e491d4f3165d62d4f5c8fd48dfeabf2ae8f42cbbd484319af33ea851b78982"}, - {file = "coverage-7.15.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:226c66e80ec0598d3b9b4874123df167ccca342aca8714f77cac6829688ee09c"}, - {file = "coverage-7.15.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ac41cc14bebda0dbfb0628036b7f75706935c95bcc07fefe9a0f93614aa60a57"}, - {file = "coverage-7.15.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8af623e5cd92080acddd02b38f2f406a2c3a0893c38950b211890361448fbf26"}, - {file = "coverage-7.15.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:07545711d4f0f32852a18f18ad11f76f0109909d09e78b9008b4cfc67e829429"}, - {file = "coverage-7.15.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a0865421cfdc53654b342d515e5a233187590882d20b95752150e53f65460017"}, - {file = "coverage-7.15.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:460115e32ee40566476db5048f9bec1e842c127ad8e6f8be745aad3ac9cbc839"}, - {file = "coverage-7.15.4-cp314-cp314t-win32.whl", hash = "sha256:cbde877ef9dd7baf272b9bfef2b8a25edd45d9170fc326951dd20eb480335e85"}, - {file = "coverage-7.15.4-cp314-cp314t-win_amd64.whl", hash = "sha256:3da9e92d1c551fd7563833e9ade686efb0c4b7363ab7681a94283958c950bf5e"}, - {file = "coverage-7.15.4-cp314-cp314t-win_arm64.whl", hash = "sha256:3a54f5a0d85050c73a38f6793090ee83974531e67fe5e57a1da9bee11398aa5e"}, - {file = "coverage-7.15.4-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:2c9872e4d9dc5d3cf616bf4b382f5a00359305a5be666a3dd0b5cdb4e49597f9"}, - {file = "coverage-7.15.4-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:e101dbb4b9b72f0cddd8cdc8c9c5b47f456766f5e0ac82dbfb75e5c55409b78a"}, - {file = "coverage-7.15.4-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7d1abebdb047729e852b9c77a00497dfbeb11eb3a117e037d7dbc3ac8e5f5c54"}, - {file = "coverage-7.15.4-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d28a4a899354d0ea6214cc59b4fa19eefbce1b9ff1688ab579acf49e894bd3fb"}, - {file = "coverage-7.15.4-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffb3c2aacea411cc7e1d27712490c11108e2de1d39019ae32915493a59a8b9ed"}, - {file = "coverage-7.15.4-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9447978a92f405d301123cfd39ff49895490efb769a758fe2734c7f631bf8ce"}, - {file = "coverage-7.15.4-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:050467a7983b8e2fe7dd41a78bb30c3e7f8c0b8cafda14b1c46f8b5e3cf2dd3c"}, - {file = "coverage-7.15.4-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:d003b7a5708ddad5c206c79607a6b92abb6fc13c57d99d8a4468cc03a2941ced"}, - {file = "coverage-7.15.4-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:c38efe30fd74e5c19e9433f11fb1f5dc9c6522770971b7c6145bbaa413dc8800"}, - {file = "coverage-7.15.4-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:1f4f826d70f772ab8b0c052329580d7fe8b8abd191e4ce0c8f81aec6614665d3"}, - {file = "coverage-7.15.4-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:4a4bf917c9953f57c957be31c1cd504e3bd2f34d4a352b9d391a3025336f6768"}, - {file = "coverage-7.15.4-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:1c9bf40ebef178a45192c75c4964760bb261b0e6ad725da5fc4c93f674f19753"}, - {file = "coverage-7.15.4-cp315-cp315-win32.whl", hash = "sha256:43619d04c3671792d2c4706ae8bf45e265dc87bbd4078189ef8b847ea1e74be2"}, - {file = "coverage-7.15.4-cp315-cp315-win_amd64.whl", hash = "sha256:be619439dbcd31a2eab10b32de9fff62c26ed4bab69dc32b8363fdaaa0882809"}, - {file = "coverage-7.15.4-cp315-cp315-win_arm64.whl", hash = "sha256:def597967dafc2e8d97c9097ea453c464e0bb8ed38f193a43070f10dc623bb6d"}, - {file = "coverage-7.15.4-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:c7dbc748ac8a1e3e59a2b28bea47675e6e778081dbbf081bde0d75def2fcbe1d"}, - {file = "coverage-7.15.4-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:2413074a5ecbb61a01a7888fc72db0ca324d13588c5b38bc0dd8564cdcdfea26"}, - {file = "coverage-7.15.4-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4e6f6f632b7b2f714bf7a1346e8f97b650ee71f3c298aaad42a2ab60f0f07645"}, - {file = "coverage-7.15.4-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8df457da2249d3c75ca2e5e835d59c725abfe92d27fdff6cd99eed85b51d5e9a"}, - {file = "coverage-7.15.4-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:050f66a08805acb5b8a23c6d4a517b1ecf82c08e81ed0e4bd727df065e5c6624"}, - {file = "coverage-7.15.4-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1587fb771d1ccceef708fdde1e5af8c7ed24b486b61d13a321acb7d8145390aa"}, - {file = "coverage-7.15.4-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8b4f1c3a69ca580f3fbd6b2046915f536d7f586874f25c1bb23add2a3c88d50f"}, - {file = "coverage-7.15.4-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:ffb58d7eff5b7f6ecc6fa21d6288ab7f968a212cb67d682c269c09b9eba3b66f"}, - {file = "coverage-7.15.4-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:d9df165544774574ee004b953023d1bebada1894a80b1052a43d798b0f676e67"}, - {file = "coverage-7.15.4-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:f9de0a24a4079b53e523b5c5e2c5945ec251ab486652659955187cf255a259bc"}, - {file = "coverage-7.15.4-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:150089274bdc9f940628552cb92844e0223c987f1902ab8efe9f45a2ec758d88"}, - {file = "coverage-7.15.4-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:a58a94fed5da6997d258e8f7668c1e195fbd04a691d781b7558f1e468f9e68bc"}, - {file = "coverage-7.15.4-cp315-cp315t-win32.whl", hash = "sha256:ebd5a6d8466ff30836572f3ba2cae8a5e8f85029b1c6d5e2ed338dc472a5166a"}, - {file = "coverage-7.15.4-cp315-cp315t-win_amd64.whl", hash = "sha256:288bde2a2d7ab6b6c2d7252fcde8b524387f2d970bdba9658fc6f8bbcaef0f9b"}, - {file = "coverage-7.15.4-cp315-cp315t-win_arm64.whl", hash = "sha256:68be5e1de60ff13c9095bbec0e5a7fa45b33b101752215b91345ea1f61c4a278"}, - {file = "coverage-7.15.4-py3-none-any.whl", hash = "sha256:964730a1e9de9c0cf11be6a1a3c79ce419c34882842abd256086ba4698705e84"}, - {file = "coverage-7.15.4.tar.gz", hash = "sha256:0548198fff07ccf4faf469520bce1c2eceb1ce3e62891921138dec10907f9d00"}, + {file = "coverage-7.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36aed4951aedf04cbe9465e76f8e71219980a52b73d07afe69746cba6ba7b97a"}, + {file = "coverage-7.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cb953835dbfa6d641ac3943e0986bc680f8abbdc2985af15b46c54985347146a"}, + {file = "coverage-7.16.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:97051c4903689b1afedc2a354d6118223051e03588078b53048603bda9014577"}, + {file = "coverage-7.16.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:770d4244c423dcafb5c31db393f429fe952b1bba23bbff7cc3886f8133769ba5"}, + {file = "coverage-7.16.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:26e7de0cb87960c6c9b5cad760068dab767b2b49a3b9376e1992c1e2691a015e"}, + {file = "coverage-7.16.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1c2c45ee1853668f0ea1a0ddff396421c9dc5ad25a56bfb94a895970c2d8e7c2"}, + {file = "coverage-7.16.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e6b2b9599e7513b0a9c5bf0357f9f8deaa4c2c821025b0693d420e6602748981"}, + {file = "coverage-7.16.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6fde65e0ea945920265dfe4a2108fc45eee2e2ea3d9c3073af6373ff9836aa71"}, + {file = "coverage-7.16.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:78103e79f9378cb0e43ddaa728629a373c070df903c5dfa98b63ba2cfb4e8c42"}, + {file = "coverage-7.16.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e40e323711b485592354069b1c027ef879cc2d11657eac09a6e5ad0b49ab7406"}, + {file = "coverage-7.16.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:c94ef980f7b94d9dab9dac076d44ca706654cd51bad19734e029084adf528c8e"}, + {file = "coverage-7.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b37ad5cbb77776f446e1b55b461eec2eef5c3e7130c72dc0e1447c3a9da2d199"}, + {file = "coverage-7.16.0-cp310-cp310-win32.whl", hash = "sha256:9421dde689e68d9fd2b6cd7d8c4498e79b5431467b6298517e3f3e60fdbe80a7"}, + {file = "coverage-7.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:81d63b68b26304e3668edb103311c17fe13c2ed1c7fe973309819f27bf61c5b8"}, + {file = "coverage-7.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:22d8802827404be32f5a4d6ddc037f6fa0074b7d06702c0224cb598def8b665d"}, + {file = "coverage-7.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a739bf08cdca0fad51b73322e4fade0102dd87794e278450b5ee87ef827954db"}, + {file = "coverage-7.16.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f99d12f8234c00b88b8077fedf288b25c77f746de312053b7db90fa756ecbdb3"}, + {file = "coverage-7.16.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7cae7715afa51dd7c9c42e6603bb46daf424c3449fdf06519cc658aa8d46e2e4"}, + {file = "coverage-7.16.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55957d350452017f523b9b03ffac078f9a214e23c04a3d0a674569203550c719"}, + {file = "coverage-7.16.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b670bd5fa93d9b6855b2837217b45a90863118e2de5e9e033aebd46d07cd08d3"}, + {file = "coverage-7.16.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe5aa402d02318db2f41e471320b2ecca6085b8f595a034c037085732e49c04a"}, + {file = "coverage-7.16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fddd26ed9a2527a7e23f7e4c1fd0734c4a5b45f77b261da1c536b20a7d2e6f0c"}, + {file = "coverage-7.16.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b2af58ecdcec37fe633d4865fccbc8c00d8aa3b31c099bcacb2720c9a0be6ab9"}, + {file = "coverage-7.16.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a3cd34b9025d62180ce2b5dae8a985bfa6cb8c05ecd57fd34ffc1ff751b5a74d"}, + {file = "coverage-7.16.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:ebaf39dd13f8af65fe5f0316b81046228ef4d91d3c3766192b418753649896d6"}, + {file = "coverage-7.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5dad64d9c17cb1983adef07998e6e2e1cf870a156f1ea80f81ce1970f4c545ce"}, + {file = "coverage-7.16.0-cp311-cp311-win32.whl", hash = "sha256:38b8e1e73750b8965d1154ed733f5303acd4e24ee2d5ee872bb1bfab744a31ce"}, + {file = "coverage-7.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:cc12e5e32acdd62fe5895939695579560639853219288519685c75b7e968d63a"}, + {file = "coverage-7.16.0-cp311-cp311-win_arm64.whl", hash = "sha256:17fc3628f99812fec24f40092af34c1c73274d331babab3d1d768a75de650cf7"}, + {file = "coverage-7.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d1c77c3579ac42798f8b7eed6d3dd258debacca32c8753fc8a1f6eaf1db644f5"}, + {file = "coverage-7.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1f81cb1554c3712e41649ed5dc98656b50b958e4da12f0f5adb681ce3db92831"}, + {file = "coverage-7.16.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6e701938ec9081d3e400a0c9a9a8ae0f7ca44214741daeac4454b1c6ef6dbd19"}, + {file = "coverage-7.16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:719a3feb6220dd32ed932d4c3676d17fb8739e2643b29c0e7c3af400ff80ac44"}, + {file = "coverage-7.16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:87771ecf986cff55e87413238cd5e4f54d949c2074bd6fc1657d26a56314ee24"}, + {file = "coverage-7.16.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:47d5e1fc0b321c8308a2aacee0497c435b08acaa629b7059798fdf6fc3006352"}, + {file = "coverage-7.16.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:01b18b8a6c9cec8d5f45550e2501426ed982cf2c35016b0acd2ba9b5d8b2fb06"}, + {file = "coverage-7.16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:32c56b5b47c50635081445ac404dd08c2d591b9c837c22570aa9e182c3b42cd4"}, + {file = "coverage-7.16.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6ad3bbad240ab937512156bc944fdee63ac4dd34a7558a3094548fd4c1150c02"}, + {file = "coverage-7.16.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4c1f16d5555a195295d0dc9c902612270e3dfed6a11f3bf7bc470b7b6a79ed3c"}, + {file = "coverage-7.16.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:f6c9c21a8bf0d19788f3c5f3e020c90317a0a63ef60521b376003801e21250fb"}, + {file = "coverage-7.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f20145a9eb5bf1fd1dde3c0bc2af2e7c22135ab07ca6284d6ada7cc3904c4e"}, + {file = "coverage-7.16.0-cp312-cp312-win32.whl", hash = "sha256:916cf8d25c1ce148f7eceb1d45afc9724841200110adc4e53250391852debd91"}, + {file = "coverage-7.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:78f8b56261d608be102c62edd3a60b66bcd0b581f3f86fdcabaf8b8d95adc950"}, + {file = "coverage-7.16.0-cp312-cp312-win_arm64.whl", hash = "sha256:577c2ac8c0036f6f8edd3a7783a9e67302b17771d1abf0fd2ed246e3158be51b"}, + {file = "coverage-7.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1545c52ce756b8a97007f439a220297f1cd72a2cbbcdffccdf1c1f70e74f9a42"}, + {file = "coverage-7.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0598aadae641f30a0796b75b45c0b9c5de8619bd5cfb251bb0cc254e86e6dd13"}, + {file = "coverage-7.16.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4080ad6bad9f14690e6b2104f5e8d137ccc65a4b5427a36662090637d4bd16d5"}, + {file = "coverage-7.16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e9883a2f8206ce3af59117dc278e5d043fea06912bca3f199816129e5e2de354"}, + {file = "coverage-7.16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:984e5430fc6f858385009e92549955157d79335b1f3e13e1031e0f89d1284261"}, + {file = "coverage-7.16.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b1374099dd1ad0d31fbb6c95d00a56a3c5e85fb3343dca14fc12f78323a2b42a"}, + {file = "coverage-7.16.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:34d8686bce035c8465b318a8c2890e69ba14a00801a27f4eb6bdc97c23944d87"}, + {file = "coverage-7.16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:857fceba6ff4b507ee0ad98798a33d544a8473df0c542bf04251ee4ed5ee6292"}, + {file = "coverage-7.16.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bbf08d951abaa1ce89e28c998361d56b952413846b459cd017f116ad4c9adbfa"}, + {file = "coverage-7.16.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1a03e78f53e4d2ab13adac19958a89322d1829913e5623d642627bf60b35da21"}, + {file = "coverage-7.16.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:dcd3dafcdd78305d27c59a1006b53a4990acb89e68d8fbe0992f4f83503c827f"}, + {file = "coverage-7.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c1bcfe470a796fbea6234accd81d258a31574dc0b7bf569e16be757572c4de17"}, + {file = "coverage-7.16.0-cp313-cp313-win32.whl", hash = "sha256:1420370276f1694b663207b8245c3628aafb9624fe3cebf313a13d860e55ee67"}, + {file = "coverage-7.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:496277c8d7beed695e02c7be53516a0152e4caef8738a0feab6a638546cce449"}, + {file = "coverage-7.16.0-cp313-cp313-win_arm64.whl", hash = "sha256:181c2906b9b3759955c1c33c51fbb91c754fbd0b82ea49e2c81061f5a052082c"}, + {file = "coverage-7.16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:54b7fba6a74d010de34319a0419d5b65af8c00f539ad0b6f39fc6f342ab99697"}, + {file = "coverage-7.16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fa4ff0b3dd52208d2b30903022d5087f82000507b504753dfeee83e4f32d6883"}, + {file = "coverage-7.16.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:35a9676bf86097f790113ebd9fb67681804ef54d40941d2f10ba68c02239e575"}, + {file = "coverage-7.16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f98d438add63546745e5e847192e3e9ab897ed6f2ca96f8281e2f5a15958ae62"}, + {file = "coverage-7.16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:151855767480be14db595cbc2040f6a4db965cdfeebd354d79b0256742b029e0"}, + {file = "coverage-7.16.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:183613f664718b340589d7f005c7e92b4b601cffd20a8a4117cfda3e983b080f"}, + {file = "coverage-7.16.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:785b114356c99c0dd5b3f57b9696cfd57b7704f4c53847df8dc88c6cc0d9bcb6"}, + {file = "coverage-7.16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:30f5aee6d1d517abcdfd4f9cad027969ff79a1440a22da263f9514e31b5b66e9"}, + {file = "coverage-7.16.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:190ffa0f5af966254c249fb3aeaca2cef389785e3e287fd577d39e134d20f8a3"}, + {file = "coverage-7.16.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:0ccc37c00e1a5d30840902c54557e104d04aead872cedf6d2281c8725a467e06"}, + {file = "coverage-7.16.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:6c60cde430c0e7e3be612973af39b4cff90ec2e2defe7b2b701daea3a0ffff04"}, + {file = "coverage-7.16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c5297028c8df849a61b29129cadfe682f90b5b396f528eb319a57d7678eefdad"}, + {file = "coverage-7.16.0-cp314-cp314-win32.whl", hash = "sha256:136988df5bc5a48795d9c42c75c4bbda5d9a78e750a080c1233010edff93a1af"}, + {file = "coverage-7.16.0-cp314-cp314-win_amd64.whl", hash = "sha256:ce2ba5e9f1842fe09165825abfb3bc6b527c71a27bc2eb3a10f2284ced64506d"}, + {file = "coverage-7.16.0-cp314-cp314-win_arm64.whl", hash = "sha256:a89d07e48d9baead9a15599923a02f62c6df6c3d85aa84ef34be3c9fd6aeb91f"}, + {file = "coverage-7.16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:6e2854b62601c89a63814ad5def3b90d99c6724cc4cb977f75b725e5fca4b1e3"}, + {file = "coverage-7.16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f093faf23df888518d273be6da65f0ec5a25b5d8b670231e4d87de07361042e7"}, + {file = "coverage-7.16.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b7dbbbf6551eb94618e7bc76ab61cc2740a5b3d13294171bd6adb36e12346c3c"}, + {file = "coverage-7.16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:51e7d0e311d2fba3915f971236cbdd4ad821fc7a23988221c0b33c964b0eba22"}, + {file = "coverage-7.16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0bb04ee77e557d7476471969d35fbbfb5fc8a4152e9409aa5811780c36d9b23e"}, + {file = "coverage-7.16.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c72c9b201dc0e8c2c8821d49858fd865010d08181bf877d2320971b6464ebfd5"}, + {file = "coverage-7.16.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0fca700cae4635656668ba6e2b66a85aac9f2622d7b2bcf82e844c409eaa1313"}, + {file = "coverage-7.16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:584896fb8b650e999e24ef57e9513e482c12f8e15a73ee9d4584e23c99465867"}, + {file = "coverage-7.16.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:949eae7e0f562b1518355aaef4b03523e49a6d3fea12aa3542d9e36c863f8267"}, + {file = "coverage-7.16.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:64f0611ee05364fc85cc3e5bc371804117a76fd337720e6017332fc7c534257a"}, + {file = "coverage-7.16.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:050a291b3cfe5e0df5999ef2fa5a7aff6e2db329f069d47eb63f02bde2e7e96b"}, + {file = "coverage-7.16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a336b1e2990a64f5c356a9b8380fb9c029d56c832b801255250c44d603271bfd"}, + {file = "coverage-7.16.0-cp314-cp314t-win32.whl", hash = "sha256:058631257350b31784ed43ceb808298b6f074edf4ebca4c7ce5082e6bf873a61"}, + {file = "coverage-7.16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:ed35097438dfa980c1ec75bc83edf8acbe7a374d7007e571957a257fbd0e2fb3"}, + {file = "coverage-7.16.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0466f4a5c0370461b7d8c7eb259d7d1db0b5756f13d66230b04d22a1d380ee11"}, + {file = "coverage-7.16.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:80d7d5d744a041f08637df743ac086204ec5acbcd8432a42b00b49e607358024"}, + {file = "coverage-7.16.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:c5feffce90c3d602e149de1c477578efc34dee5f069f9764cc15808ce01ee15c"}, + {file = "coverage-7.16.0-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:acadbf2f2a18d7f9c7f119ac798c00c540d7c79c93abd71ed648c87891303633"}, + {file = "coverage-7.16.0-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4212cec9b42fd9929e70b462732fefd8b13406371871c82f3c14397499d6550b"}, + {file = "coverage-7.16.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c5a43cc0ef101637ae920a9eed24cf0549ef815621eae68b3ad577ec5a7ad2f"}, + {file = "coverage-7.16.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c76a9b50a344261fe4a9bd20c322b48d3913cc48e8c37f78c21a596008296e68"}, + {file = "coverage-7.16.0-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:80cf547379ad6b1878fd03b033b51188beab4b41824c96e7839e014a4cb947be"}, + {file = "coverage-7.16.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:4b1d09cb5d8dc2c7164450f5217e6f0717497de9c588806a0780d352abef904a"}, + {file = "coverage-7.16.0-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:cd1e85abed2d2499c16664137ac802356316f92b4e2bf3c150bdf0c45f5dd9ae"}, + {file = "coverage-7.16.0-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:360967a6fd77794c167529eec2d16ff8e38216110619d23acc3fd466a1648bee"}, + {file = "coverage-7.16.0-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:92cbc2bf4f7f67c79f1d3ca4fe8c50faddf48e852a3d07eaaf02dc014889832f"}, + {file = "coverage-7.16.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:cce4dc8528453128c6fae523b15f3887fbea1d4d7c9eb9639d3d4fdcbe570c73"}, + {file = "coverage-7.16.0-cp315-cp315-win32.whl", hash = "sha256:5205baea687133613dced668a3d0168ea1479349615bfc255849a7944988c889"}, + {file = "coverage-7.16.0-cp315-cp315-win_amd64.whl", hash = "sha256:4fcb5f07a9b7083bfb715115d27ce263ba2b5b89dddeee536b295ba0e3c2c627"}, + {file = "coverage-7.16.0-cp315-cp315-win_arm64.whl", hash = "sha256:d568a8adcec0eda42ec23e5e65dfb8c184fc255120f9e99b484f7c869d923fb9"}, + {file = "coverage-7.16.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:3e8037e8213adf882e9d7eedd2c5c557933ab0b9632c42d98fe98ec9bcdb4025"}, + {file = "coverage-7.16.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:289f2ed4d56eebf029b649e7dfc3c1153b111962a75e294cdd8e4a1598a04cc3"}, + {file = "coverage-7.16.0-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9b83f6ac575530783771c8dcf05284f7c8b5b12f1e7cb226d63445aac4497a3a"}, + {file = "coverage-7.16.0-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c3ff6580f2dfc5bec34717b85b2e6cf5ec993b721e7bb58a794babd525a8178"}, + {file = "coverage-7.16.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:507596cee23e9968b1934fe86d799b76166541af0a293930918b1b48a5c84bd2"}, + {file = "coverage-7.16.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edc2be98e6c55ccc5ff7832bb64f023a4b03dba39dfa84b850046cf08a8249b0"}, + {file = "coverage-7.16.0-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9c0690994b84a15a53bdd39e0b2fdb539b22533820623eb86ba75b93760c645b"}, + {file = "coverage-7.16.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:de24c62bf798940a14674a47489a81b79915ec4134f556d5199830e065225dd0"}, + {file = "coverage-7.16.0-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:69474d81f198774c9d2937599ca5da04c9e1c5de5032da23c607ce4960ce360e"}, + {file = "coverage-7.16.0-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:72a0795cc6d34acc2b03dfeabdc82b61b72087f2737018b56ac92c1cf5446c54"}, + {file = "coverage-7.16.0-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:d9a218d3f9c7d6916684ed5ba94f620661117a730e733cd6ef5e87accc5872eb"}, + {file = "coverage-7.16.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:49fa72ead28c8216f8916398a4f3c4669acb30a061822810ee20a727a1be2897"}, + {file = "coverage-7.16.0-cp315-cp315t-win32.whl", hash = "sha256:27461af9f3ed7d2cf2411eb083784f87055ebf42211789ae3a216c48609bc743"}, + {file = "coverage-7.16.0-cp315-cp315t-win_amd64.whl", hash = "sha256:c5612cc20ca76abc883e50269af47c1494b42958bb63dbb9aa79729a1ab5f7d3"}, + {file = "coverage-7.16.0-cp315-cp315t-win_arm64.whl", hash = "sha256:2ddaa9e2af4760a329d80008b7a3b4762fbb0dbcb169199360f9a5179c32f2dc"}, + {file = "coverage-7.16.0-py3-none-any.whl", hash = "sha256:245f7de6d023a5bba375dbec9f2e0869bfa26ac0cc639bbb7b4c814884000b73"}, + {file = "coverage-7.16.0.tar.gz", hash = "sha256:077f0964087883176ff6ab9b074694cae29f8c708273b13ca62c183c6ed716cd"}, ] [package.extras] @@ -708,7 +705,7 @@ version = "2.8.0" description = "DNS toolkit" optional = false python-versions = ">=3.10" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af"}, {file = "dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f"}, @@ -729,7 +726,7 @@ version = "2.3.0" description = "A robust email address syntax and deliverability validation library." optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4"}, {file = "email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426"}, @@ -770,7 +767,7 @@ version = "0.16.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, @@ -782,7 +779,7 @@ version = "1.0.9" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"}, {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"}, @@ -804,7 +801,7 @@ version = "0.27.2" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, @@ -841,18 +838,18 @@ tests = ["freezegun", "pytest (>=9)", "pytest-benchmark", "pytest-codspeed", "py [[package]] name = "idna" -version = "3.18" +version = "3.19" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.9" groups = ["main", "dev"] files = [ - {file = "idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2"}, - {file = "idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848"}, + {file = "idna-3.19-py3-none-any.whl", hash = "sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4"}, + {file = "idna-3.19.tar.gz", hash = "sha256:5e0811a4383b21dc5838069f801c4fb62113b7447663d2530d2bd6e77b49bf15"}, ] [package.extras] -all = ["mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] +all = ["coverage (>=7.10.0)", "hypothesis (>=6.141.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.16.0)", "ty (>=0.0.37)"] [[package]] name = "iniconfig" @@ -1090,7 +1087,7 @@ version = "30.4.4" description = "license-expression is a comprehensive utility library to parse, compare, simplify and normalize license expressions (such as SPDX license expressions) using boolean logic." optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "license_expression-30.4.4-py3-none-any.whl", hash = "sha256:421788fdcadb41f049d2dc934ce666626265aeccefddd25e162a26f23bcbf8a4"}, {file = "license_expression-30.4.4.tar.gz", hash = "sha256:73448f0aacd8d0808895bdc4b2c8e01a8d67646e4188f887375398c761f340fd"}, @@ -1104,25 +1101,22 @@ dev = ["Sphinx (>=5.0.2)", "doc8 (>=0.11.2)", "pytest (>=7.0.1)", "pytest-xdist [[package]] name = "linkify-it-py" -version = "2.1.0" +version = "2.2.0" description = "Links recognition library with FULL unicode support." optional = false python-versions = ">=3.10" groups = ["dev"] markers = "platform_system != \"Windows\"" files = [ - {file = "linkify_it_py-2.1.0-py3-none-any.whl", hash = "sha256:0d252c1594ecba2ecedc444053db5d3a9b7ec1b0dd929c8f1d74dce89f86c05e"}, - {file = "linkify_it_py-2.1.0.tar.gz", hash = "sha256:43360231720999c10e9328dc3691160e27a718e280673d444c38d7d3aaa3b98b"}, + {file = "linkify_it_py-2.2.0-py3-none-any.whl", hash = "sha256:3adc40eb5af300b2605fcfdb968c24e1d780a90f1f2221af7c15e5111e94d443"}, + {file = "linkify_it_py-2.2.0.tar.gz", hash = "sha256:907acd2d17ac1fbb9ddb62c8957ccbd6158cac602231a15c3b0cd1e215f03cee"}, ] -[package.dependencies] -uc-micro-py = "*" - [package.extras] benchmark = ["pytest", "pytest-benchmark"] dev = ["black", "flake8", "isort", "pre-commit", "pyproject-flake8"] doc = ["myst-parser", "sphinx", "sphinx_book_theme"] -test = ["coverage", "pytest", "pytest-cov"] +test = ["coverage", "pytest", "pytest-cov", "pytest-timeout"] [[package]] name = "markdown-it-py" @@ -1477,14 +1471,14 @@ icu = ["PyICU (>=1.0.0)"] [[package]] name = "packaging" -version = "24.2" +version = "26.3" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, - {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, + {file = "packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c"}, + {file = "packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79"}, ] [[package]] @@ -1623,15 +1617,15 @@ files = [ [[package]] name = "platformdirs" -version = "4.11.3" +version = "4.11.7" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.10" groups = ["dev"] markers = "platform_system != \"Windows\"" files = [ - {file = "platformdirs-4.11.3-py3-none-any.whl", hash = "sha256:5ed065d443751de711da036041a7a214122efc4a4de393b3f4137ba5576540e7"}, - {file = "platformdirs-4.11.3.tar.gz", hash = "sha256:66a73d38a849810252df809a3d8bcbda8e26f6c189920e7535ad608a48dbb5ab"}, + {file = "platformdirs-4.11.7-py3-none-any.whl", hash = "sha256:8a02cb259042c79d1cd0450facc2fe6dc9d303ae7901afbe33bf8ea0b188cef6"}, + {file = "platformdirs-4.11.7.tar.gz", hash = "sha256:4f41487eeeeeb07f3a6625e61d9bc0ae6809f92d3386dbd74392fbb76108104d"}, ] [[package]] @@ -1652,17 +1646,19 @@ testing = ["coverage", "pytest", "pytest-benchmark"] [[package]] name = "prometheus-client" -version = "0.21.1" +version = "0.24.1" description = "Python client for the Prometheus monitoring system." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "prometheus_client-0.21.1-py3-none-any.whl", hash = "sha256:594b45c410d6f4f8888940fe80b5cc2521b305a1fafe1c58609ef715a001f301"}, - {file = "prometheus_client-0.21.1.tar.gz", hash = "sha256:252505a722ac04b0456be05c05f75f45d760c2911ffc45f2a06bcaed9f3ae3fb"}, + {file = "prometheus_client-0.24.1-py3-none-any.whl", hash = "sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055"}, + {file = "prometheus_client-0.24.1.tar.gz", hash = "sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9"}, ] [package.extras] +aiohttp = ["aiohttp"] +django = ["django"] twisted = ["twisted"] [[package]] @@ -1671,7 +1667,7 @@ version = "26.2.16" description = "ISO country, subdivision, language, currency and script definitions and their translations" optional = false python-versions = ">=3.10" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "pycountry-26.2.16-py3-none-any.whl", hash = "sha256:115c4baf7cceaa30f59a4694d79483c9167dbce7a9de4d3d571c5f3ea77c305a"}, {file = "pycountry-26.2.16.tar.gz", hash = "sha256:5b6027d453fcd6060112b951dd010f01f168b51b4bf8a1f1fc8c95c8d94a0801"}, @@ -1679,20 +1675,20 @@ files = [ [[package]] name = "pydantic" -version = "2.13.4" +version = "2.13.5" description = "Data validation using Python type hints" optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["main", "dev"] files = [ - {file = "pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba"}, - {file = "pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6"}, + {file = "pydantic-2.13.5-py3-none-any.whl", hash = "sha256:346a034f080da3755d8e9cb5e00e8b07de1d39e4f6e2c87d8ab7cafa0b269a73"}, + {file = "pydantic-2.13.5.tar.gz", hash = "sha256:51a9c5f7b2f8e636f04c6cada605d9b6a3bf1348fdf945a3d8869b19bba0ee08"}, ] [package.dependencies] annotated-types = ">=0.6.0" email-validator = {version = ">=2.0.0", optional = true, markers = "extra == \"email\""} -pydantic-core = "2.46.4" +pydantic-core = "2.46.5" typing-extensions = ">=4.14.1" typing-inspection = ">=0.4.2" @@ -1702,132 +1698,132 @@ timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows [[package]] name = "pydantic-core" -version = "2.46.4" +version = "2.46.5" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["main", "dev"] files = [ - {file = "pydantic_core-2.46.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a396dcc17e5a0b164dbe026896245a4fa9ff402edca1dff0be3d53a517f74de4"}, - {file = "pydantic_core-2.46.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:da4b951fe36dc7c3a1ccb4e3cd1747c3542b8c9ceede8fc86cae054e764485f5"}, - {file = "pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb63e0198ca18aad131c089b9204c23079c3afa95487e561f4c522d519e55aba"}, - {file = "pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f47286a97f0bc9b8859519809077b91b2cefe4ae47fcbf5e466a009c1c5d742b"}, - {file = "pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:905a0ed8ea6f2d61c1738835f99b699348d7857379083e5fc497fa0c967a407c"}, - {file = "pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea793e075b70290d89d8142074262885d3f7da19634845135751bd6344f73b50"}, - {file = "pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395aebd9183f9d112f569aeb5b2214d1a10a33bec8456447f7fbdfa51d38d4cd"}, - {file = "pydantic_core-2.46.4-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:b078afbc25f3a1436c7a1d2cd3e322497ee99615ba97c563566fdf46aff1ee01"}, - {file = "pydantic_core-2.46.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f747929cf940cddb5b3668a390056ddd5ba2e5010615ea2dcf4f9c4f3ab8791d"}, - {file = "pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:daa27d92c36f24388fe3ad306b174781c747627f134452e4f128ea00ce1fe8c4"}, - {file = "pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:19e51f073cd3df251856a8a4189fbdf1de4012c3ebacfb1884f94f1eb406079f"}, - {file = "pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1747f85cee84c26985853c6f3d9bd3e75da5212912443fa111c113b9c246f39"}, - {file = "pydantic_core-2.46.4-cp310-cp310-win32.whl", hash = "sha256:2f84c03c8607173d16b5a854ec68a2f9079ae03237a54fb506d13af47e1d018d"}, - {file = "pydantic_core-2.46.4-cp310-cp310-win_amd64.whl", hash = "sha256:8358a950c8909158e3df31538a7e4edc2d7265a7c54b47f0864d9e5bae9dcebf"}, - {file = "pydantic_core-2.46.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594"}, - {file = "pydantic_core-2.46.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c"}, - {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826"}, - {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04"}, - {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e"}, - {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3"}, - {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4"}, - {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398"}, - {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3"}, - {file = "pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848"}, - {file = "pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3"}, - {file = "pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109"}, - {file = "pydantic_core-2.46.4-cp311-cp311-win32.whl", hash = "sha256:041bde0a48fd37cf71cab1c9d56d3e8625a3793fef1f7dd232b3ff37e978ecda"}, - {file = "pydantic_core-2.46.4-cp311-cp311-win_amd64.whl", hash = "sha256:6f2eeda33a839975441c86a4119e1383c50b47faf0cbb5176985565c6bb02c33"}, - {file = "pydantic_core-2.46.4-cp311-cp311-win_arm64.whl", hash = "sha256:14f4c5d6db102bd796a627bbb3a17b4cf4574b9ae861d8b7c9a9661c6dd3362d"}, - {file = "pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2"}, - {file = "pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f"}, - {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7"}, - {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7"}, - {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712"}, - {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4"}, - {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce"}, - {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987"}, - {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b"}, - {file = "pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458"}, - {file = "pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b"}, - {file = "pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c"}, - {file = "pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894"}, - {file = "pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89"}, - {file = "pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a"}, - {file = "pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008"}, - {file = "pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4"}, - {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76"}, - {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3"}, - {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76"}, - {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4"}, - {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a"}, - {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262"}, - {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e"}, - {file = "pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd"}, - {file = "pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be"}, - {file = "pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d"}, - {file = "pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb"}, - {file = "pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292"}, - {file = "pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d"}, - {file = "pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb"}, - {file = "pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462"}, - {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9"}, - {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4"}, - {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914"}, - {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28"}, - {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b"}, - {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c"}, - {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb"}, - {file = "pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898"}, - {file = "pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e"}, - {file = "pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519"}, - {file = "pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4"}, - {file = "pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac"}, - {file = "pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596"}, - {file = "pydantic_core-2.46.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:fd8b3d9fd264be37976686c7f65cd52a83f5e84f4bfd2adf9c1d469676bbb6ae"}, - {file = "pydantic_core-2.46.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9f444c499b3eefd3a92e348059471ea0c3a6e303d9c1cec09fa748fd9f895201"}, - {file = "pydantic_core-2.46.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3447661d99f75a3683a4cf5c87da72f2161964611864dbbeac7fbb118bb4bfc0"}, - {file = "pydantic_core-2.46.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b9bab013d1c7a79d3501ff86d0bc9c31bf587db4551677b96bec07df78c6b15"}, - {file = "pydantic_core-2.46.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d995260fdf4e1db774581b4900e0f832abe3c7c84996726bbc161b19c8f29e76"}, - {file = "pydantic_core-2.46.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f13a646d65d09fbf1bc6b3a9635d30095c8e7e5cc419ff35ecc563c5fd04cd49"}, - {file = "pydantic_core-2.46.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432c179df7874eeb73307aad2df0755e1ae0efa61ff0ea89b93e194411ae3928"}, - {file = "pydantic_core-2.46.4-cp39-cp39-manylinux_2_31_riscv64.whl", hash = "sha256:e68b7a074f65a2fd746c52a7ce6142ab7006074ac269ace0c25cd8ba171f8066"}, - {file = "pydantic_core-2.46.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4a05d69cba51d852c5c3e92758653245a50c0b646ced0cf05bd793ed592839d6"}, - {file = "pydantic_core-2.46.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:228ee9bae8bef5b1e97ec58302f80357c37199e0d0a99174e138d28e6957b9d9"}, - {file = "pydantic_core-2.46.4-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:10e17cbb10a330363733efc4d7c4d0dd827ac0909b8f6a6542298fed1ea62f29"}, - {file = "pydantic_core-2.46.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:91a06d2e259ecfbd8c901d70c3c507900458498142b3026a296b7de4d1322cc9"}, - {file = "pydantic_core-2.46.4-cp39-cp39-win32.whl", hash = "sha256:d80ee3d731373b24cebbc10d689ca4ee1875caf0d5703a245db18efd4dd37fc1"}, - {file = "pydantic_core-2.46.4-cp39-cp39-win_amd64.whl", hash = "sha256:3be77f45df024d789a672ae34f8b06fb346c4f9f46ea714956660ea4862e89ac"}, - {file = "pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:14d4edf427bdcf950a8a02d7cb44a08614388dd6e1bdcbf4f67504fa7887da9c"}, - {file = "pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0ce40cd7b21210e99342afafbd4d0f76d784eb5b1d60f3bdc566be4983c6c73b"}, - {file = "pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90884113d8b48f760e9587002789ddd741e76ab9f89518cd1e43b1f1a52ec44b"}, - {file = "pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66ce7632c22d837c95301830e111ad0128a32b8207533b60896a96c4915192ea"}, - {file = "pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7"}, - {file = "pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df"}, - {file = "pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526"}, - {file = "pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0"}, - {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0"}, - {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7"}, - {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2"}, - {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9"}, - {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf"}, - {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30"}, - {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc"}, - {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983"}, - {file = "pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1"}, + {file = "pydantic_core-2.46.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:657b40d6240c0a7b6a64b30f22d1e3aa631c7e846c621b0c0f6d1d75e2e15ea6"}, + {file = "pydantic_core-2.46.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ecb42011e12ee19cafbc312887cbf3546959fe02fbad44f272d4be5baa997615"}, + {file = "pydantic_core-2.46.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dedce55295becb61921e386b99d4f2706045306e7fa52249a33004c837379fb"}, + {file = "pydantic_core-2.46.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9f47b8a949e60f027f0aa0a6f6c7b7e9c55cbf4380d10b344e282fa4e7ab1e1b"}, + {file = "pydantic_core-2.46.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:200aa3dc9f8d54f0754f43247c0bad0999fdcfbfd2488384dd44f37279271fe6"}, + {file = "pydantic_core-2.46.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6d30e1a4f138b8951063e9a394752a9179b51da288ffa507b1e659222f4c1793"}, + {file = "pydantic_core-2.46.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:850a08d167dde16db8702c274f320c7be9d7da6f6dff2b58b18f9e815bd94f5b"}, + {file = "pydantic_core-2.46.5-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:c3471e5c4a949c26ec00a77f01df59096aa9495877de76fd60a980f8ee6be461"}, + {file = "pydantic_core-2.46.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3a3e26b6a8274211bddee2d0e4d0d42778f17a34510f49d2ec44b58abfc41736"}, + {file = "pydantic_core-2.46.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fc5d783bd4a2387e97b8a2d5ec781cfb92b3d893bf82370548e99db5915935d3"}, + {file = "pydantic_core-2.46.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:356c8368cbc321050b169595683a2e1d63413b1e0e2868b330af9fc14c616d3f"}, + {file = "pydantic_core-2.46.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:eb7d8d0e5886a89a55d2eef490e272fa965a9d57c6b29a5b5088a7997ec2cad1"}, + {file = "pydantic_core-2.46.5-cp310-cp310-win32.whl", hash = "sha256:4d44cf99ddebf875f9b68cc267aa684c99b7b44fe63ee1cac4ec163807290069"}, + {file = "pydantic_core-2.46.5-cp310-cp310-win_amd64.whl", hash = "sha256:1e5aad1220a1192c42341c8fd4a8686657e73ab2a920c970bdc4de334fe3193d"}, + {file = "pydantic_core-2.46.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a1dee1b804ff4d11c663636cf15d2ea47e9f79cd56c033fb1cbf08924842a48f"}, + {file = "pydantic_core-2.46.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d625a186a65201c23a9e3b8ed9c47e90a026e03256608cc91851c6709096844f"}, + {file = "pydantic_core-2.46.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f8507560a9284e1370bb048ed4282012fbef4e8d109875b95e884d228552061"}, + {file = "pydantic_core-2.46.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f93c5fe914d75fbec9a49209b00da5f08e9e467d69da2b1510c81940cfd10be"}, + {file = "pydantic_core-2.46.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aca6c767f552b21b10f774aeac128e828eafb796adfa1b666a18bf6321453c3a"}, + {file = "pydantic_core-2.46.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:701b2e04b560eeb4bddf7a25ab8ca476176e34fdbd9a0e18196f0d12d4685f0b"}, + {file = "pydantic_core-2.46.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49776eab08766a08dfff7012f8b422dcd7e25e43b316eedf0477c24fcfa84b7c"}, + {file = "pydantic_core-2.46.5-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:a2468d93d181667a7abd66e1b64bb9f76f361b0fef8faddf687456453576f5ee"}, + {file = "pydantic_core-2.46.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:53feb344243bb9510a9dec7bf3cf1b64d88a98af5dc7872a5160465f8b198c8e"}, + {file = "pydantic_core-2.46.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cd5214352ae68f3b5e9af7768bdc5253695ee069675db3480518420b3be881f2"}, + {file = "pydantic_core-2.46.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:9432f3598db432cb51c5b37fdbf29a60fcccc79e30d37a05022776a6bc4ab689"}, + {file = "pydantic_core-2.46.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8feeac04b5794e513e710af2f9c87d49f31a6dc47967bb264a1fed61a8989bec"}, + {file = "pydantic_core-2.46.5-cp311-cp311-win32.whl", hash = "sha256:892a881d5f68c2b9ea304b7a6c2c60d9343df578a311b0f86b94bc8f1ffe8129"}, + {file = "pydantic_core-2.46.5-cp311-cp311-win_amd64.whl", hash = "sha256:40375c2d05acec10323e45dfe2077ac44bc74659008614af5069034e2cfc781c"}, + {file = "pydantic_core-2.46.5-cp311-cp311-win_arm64.whl", hash = "sha256:28a6a556cd3b6066bea827857f9d9cce027c96f776e512f544a581f9e42161f8"}, + {file = "pydantic_core-2.46.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b9fe6fb92520e3fd61f2e49000b6911b188824f089b75973ea06d6267f0b476d"}, + {file = "pydantic_core-2.46.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a39ac25a9a2fa4072efdb429833c4a4c8009a51ff9eea3eeae131713cd27991e"}, + {file = "pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fdc8b93a41521988916eeaa271173fcca7fa0803d62f87675aac8dcec1c8e29"}, + {file = "pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b98134087d9de723658d17a42c7d0da8d6e2ef08015dee7dc93889047315f5e4"}, + {file = "pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e652ab17569c94bff5475520f907b7148b8c24036a8ebbe5cf7cf7493d28579a"}, + {file = "pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d925f3d9afd05a8c0fb3a1031463a8d59ebe5e2afad297e29c78be19e13b4e62"}, + {file = "pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fc5be0abd4a407e200d844b404e33639a554e7bd0d448e7b9ae181be4789ac2"}, + {file = "pydantic_core-2.46.5-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:816ff0a6550ffc06c098ccd2e0698600f9aa7da192a79eaa6f9af504a35db869"}, + {file = "pydantic_core-2.46.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c7ea57fc63aa7da93a1bd2d644e6577befae10c52c4e36377635eea1056a74f5"}, + {file = "pydantic_core-2.46.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:efd62a42486f1bda5d24cb4f63d15a3c7768375fe83d36f9417b4ad7a2fb20b3"}, + {file = "pydantic_core-2.46.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:2bc9419666990c06d7397831f2126a1ecc3594aaa3ff7de5bf2d066802f4e07b"}, + {file = "pydantic_core-2.46.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:18a09e1e1011b462f2e32774f25859ef1223d5c2b0546a633cf56654710721e0"}, + {file = "pydantic_core-2.46.5-cp312-cp312-win32.whl", hash = "sha256:5cb482e9e84c851f4e623fe4acc1ced89168cf1fe18f7089db4548c8f5bbb65b"}, + {file = "pydantic_core-2.46.5-cp312-cp312-win_amd64.whl", hash = "sha256:5e81740c09e310f5aa5cbd3e434a01c154d4bef93241c7877b39f211d2b78ba8"}, + {file = "pydantic_core-2.46.5-cp312-cp312-win_arm64.whl", hash = "sha256:f7b0ec93a2893de856652154d73b7ba622f26fa97726487dcac373de5f4c6084"}, + {file = "pydantic_core-2.46.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:b7ca9034437b6022f941f4857459562ee00a560b97e7cce8a0ec5a74fc6766e0"}, + {file = "pydantic_core-2.46.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f332f0e72a5a0400141f830744e141bf9f97917878dbe968669e8a7fefea78ff"}, + {file = "pydantic_core-2.46.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:193375f3548919d3f0b60936ca113ada3e38f264f91b9b8e0508efaad57be931"}, + {file = "pydantic_core-2.46.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:79bdfa52f843137045b2d081cc05c120ba6665d29b7559c2c47690906f39279f"}, + {file = "pydantic_core-2.46.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:24922243639cbdac66c75fcb6fd6495a9cb52b213d62f9a0d16f0310b1ff8038"}, + {file = "pydantic_core-2.46.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c76fe65e607be28c7fd4d56fc3c42b1583aa058ce3408b7ad0fd540171d31f9f"}, + {file = "pydantic_core-2.46.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f7b393a8b3da82f5c1fc0751e6d01ac6c55b93c18226a60bdfba4a724efafd1"}, + {file = "pydantic_core-2.46.5-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:7ac031912d54f3d83ef3b3eb98dfabc1608802e2202263d25957eeed40b94761"}, + {file = "pydantic_core-2.46.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:837b396ca3d7b74091ca623f6cbd8351bd42d670a79c2683e79fb089f06a2de5"}, + {file = "pydantic_core-2.46.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:5ee239d575f80b08eca11f6e20f90c4c695de7825c67eefe6091fbf20dda648e"}, + {file = "pydantic_core-2.46.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:e80675d75ae2cd14372cb65cad5400d9347a3d3f6c13000183f22dfd027283ed"}, + {file = "pydantic_core-2.46.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:9c4b71f10dd532fb7a5cbc8f58707779e64f03a258c2bf8bfbaecfcd9970b519"}, + {file = "pydantic_core-2.46.5-cp313-cp313-win32.whl", hash = "sha256:97bf8de4d541598c94a59344eeb988a94c08ff76b5723c41f6567ec18c7892ea"}, + {file = "pydantic_core-2.46.5-cp313-cp313-win_amd64.whl", hash = "sha256:15f4a94963c95accac15b7b657bb177d3ad82bb90b0d0526d9a9b85079925db5"}, + {file = "pydantic_core-2.46.5-cp313-cp313-win_arm64.whl", hash = "sha256:d22a945598fb91236b4dd793a6e42e4f3dd7740bb5aace5ebd7d4c08d13bb575"}, + {file = "pydantic_core-2.46.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:c1c43ad4339643d70ebb8124e1305a7dab423001eff58bb41a0f731adbc98355"}, + {file = "pydantic_core-2.46.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1a353f84de772f423b5ffb11d7ae352fbbef0f446f3c0b0af0f8236d7233606e"}, + {file = "pydantic_core-2.46.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5086029a57366b8cf81b130a43908738095c270c21a8d7f0e8bdfdb89718e2f3"}, + {file = "pydantic_core-2.46.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46c25dda9d092a06c08db76ffe0a197107904d0dfac653f7d5306bbcd6d6119c"}, + {file = "pydantic_core-2.46.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:37ea7b83c935e5b0d68c9449b82651accf78a10828b2c02b2f2d9e9496446c21"}, + {file = "pydantic_core-2.46.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e64e88d5585bea9ce95861079de72006c7fa6d3df4e3a3b65ba31eb979c15c9f"}, + {file = "pydantic_core-2.46.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54d510bac3ee52247af28ed4bb18a1e799f040ac60fd2bf5ccd4c92f1fbe786f"}, + {file = "pydantic_core-2.46.5-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:a2a5e1d0ff29adddc9f6d6821a66302e4493f8ca898b715b6b1182c2c201ea0a"}, + {file = "pydantic_core-2.46.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:03b9666e41e35d8909852ba191a0607520f81b74eaf12ccf8737005dbb313821"}, + {file = "pydantic_core-2.46.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:a91c17edf6eea2402cb5457b4c89e99bc5ed1004aa34c4adf1d4258c1a5c22c2"}, + {file = "pydantic_core-2.46.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b49924c73a235e969511bf2aabdff3beebf9820931f646c80274d5d780010c47"}, + {file = "pydantic_core-2.46.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:2cbd9a5eff05e51c447c34dfa4632145b26b09120cf04bd0c871e44c1a5e1c9a"}, + {file = "pydantic_core-2.46.5-cp314-cp314-win32.whl", hash = "sha256:2d5d76654becf5efd62c9e51c3756c67b49498b0c9a40884934c40807adbd074"}, + {file = "pydantic_core-2.46.5-cp314-cp314-win_amd64.whl", hash = "sha256:fa10ef4112775900e7a0661068635eb67b2ab824fbde764de6e0e21982a93db0"}, + {file = "pydantic_core-2.46.5-cp314-cp314-win_arm64.whl", hash = "sha256:045ab3b6d308439e32b81cc173bba5b9018bc6ed896afd0c65b3b009b1699af5"}, + {file = "pydantic_core-2.46.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8816f3d218beb4b787de5c9759c259b8fa61f9dec42dc7811f320a33771778b7"}, + {file = "pydantic_core-2.46.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:bce57638e08ac148e5778cce7feb968307a727d66f8e2274a543d0cf0c9ad6a3"}, + {file = "pydantic_core-2.46.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:976e1128455aa595ea04c79ccfedff1aaeab96ee013fcc916bed120c4f0ad94f"}, + {file = "pydantic_core-2.46.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b891faeedeafba41b2983e5001a81b6a915b69544c7e7570d1989ce1c36ac7"}, + {file = "pydantic_core-2.46.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f194189415698233dd1114a093a9b56e61e2c57e11b469be3b0506f46f0771c"}, + {file = "pydantic_core-2.46.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82a36973cf8a2ef5406f4fe2edbf8ed0c99629535d959e0b100c76a32535a111"}, + {file = "pydantic_core-2.46.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdbb78909f52b981d3b2d56b97328d71eb0b974c36bd77c920123a7ebb192829"}, + {file = "pydantic_core-2.46.5-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:52e24eacdb536cade636aa90fb851835222becff8484b7001fdc78cb0290f2aa"}, + {file = "pydantic_core-2.46.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:37ae34309d7bd8c0d61ab839668058f2a7962ea1fc51d105d2db228fe0618034"}, + {file = "pydantic_core-2.46.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:0cdbada856a1c69a7624a64d3d9aefe79300bd6ef827b43a4f265010b9b55184"}, + {file = "pydantic_core-2.46.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:545f26c504b27c3758439a5e6d9349931f0a04f855668d5fe323c89e82300a38"}, + {file = "pydantic_core-2.46.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:ff218293c9c806138dca139765e3b067621be52bcd93cdc14c7711be7ddc90a9"}, + {file = "pydantic_core-2.46.5-cp314-cp314t-win32.whl", hash = "sha256:97cf3eb53a8cccacf9d46686a0926186c9bfb5574f2ed66d3639d5fe117cd3a9"}, + {file = "pydantic_core-2.46.5-cp314-cp314t-win_amd64.whl", hash = "sha256:d2f9fc07a8042a8f95925b35c4f04f469707c981fc33245b6ca187cf5d2dd290"}, + {file = "pydantic_core-2.46.5-cp314-cp314t-win_arm64.whl", hash = "sha256:acf8a67ba51f4ca9ddbd0e6b3000a65ac51ab734661778b3e7ba64d99a710f2f"}, + {file = "pydantic_core-2.46.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c583b927a8838dab890706a6fa7573fbb8b70e24000ef9f7238e2d6f6435a5ed"}, + {file = "pydantic_core-2.46.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cdc8b74ecc48c0cb1e9607a05ec4e9e88db60a19ffcc9a1d5f9088ede40c8dc0"}, + {file = "pydantic_core-2.46.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b10e3e8fd7ddc2bd915848a2768e44c15b22936f1cc54c462ad1164deb02655"}, + {file = "pydantic_core-2.46.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f077d0b97ab11fa7dcc633fca53515f290bca8a8a633e966d5b6d1879d9ed01a"}, + {file = "pydantic_core-2.46.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7b0fc826b16c55e561e5d2a0c5c77b051ba1d92808118c4e4b5390f5e0cf191d"}, + {file = "pydantic_core-2.46.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef3fbbf161dc9351a2fe0422e51b129f9e97e42385bd0320b309c15f7d287dd8"}, + {file = "pydantic_core-2.46.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:978e7b97d4824b5be09c69fb70507cbde3b0323fc147332ca40a94d9a6a0ebbf"}, + {file = "pydantic_core-2.46.5-cp39-cp39-manylinux_2_31_riscv64.whl", hash = "sha256:9b68938dd5b0c783d88ff8e2dcc69451b5eb936fe212d516b21b9d5567f6d464"}, + {file = "pydantic_core-2.46.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:771cf63ae0b1b50dd22e5f3e3549fab5f3f4ff1635d352a9e1a97fe01c7b2e64"}, + {file = "pydantic_core-2.46.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7c6be839a5a8312626b32029a415644a0846b420bc8b52b95b28cd92da162168"}, + {file = "pydantic_core-2.46.5-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:895395f8918627b04efb1ad2a4cf605387143300ba03304cd1dfa6d03f5e095e"}, + {file = "pydantic_core-2.46.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:fc8515076c11f3cfdf4fb142dcca0fe384b1230a3b5415458ac84f3e0903ec13"}, + {file = "pydantic_core-2.46.5-cp39-cp39-win32.whl", hash = "sha256:3d2652072b2d774947ba5cf78a9e59644ac62ee572daf6dd2e1dfe905e15b2b7"}, + {file = "pydantic_core-2.46.5-cp39-cp39-win_amd64.whl", hash = "sha256:3aa166e99c4f2985407fb8714aebede877ecb5455cf321b606adca926d30d5a0"}, + {file = "pydantic_core-2.46.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:c14ad3bdc85ee7f318742c457ca3968a92126d144b15721c759033bfb06296c2"}, + {file = "pydantic_core-2.46.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0bddb4020d8f04175865ccd17eff3040874fc11fb593f424edb452653b4b947c"}, + {file = "pydantic_core-2.46.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2471fd51c61c610e1dcf7de44d7299283661654d11264ab4802b303368d69c47"}, + {file = "pydantic_core-2.46.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b10ec717381bdbfafef34607824db4c91de69ff085e4fca3b2af91b4fa17e68a"}, + {file = "pydantic_core-2.46.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:013d6f3483d81e02e7c328831808f336c8596ee33b4bd4026b9ffb1e960b8942"}, + {file = "pydantic_core-2.46.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:e9c134bb666dd54b778b9fc0d2b50cbb7f979b9e3716f26a88c9ab3b6fc1dd0f"}, + {file = "pydantic_core-2.46.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:347ec774390c87326a2e4929d58d3f7e8763a104d5d35f4cd595a4c952366433"}, + {file = "pydantic_core-2.46.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e24d8f05fa2d28513d94e877e9c75ad66175376209b3977f916e240e623193c"}, + {file = "pydantic_core-2.46.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ab4b66edffb32d9e951efb3814bd104b8367a7501b81b955cacb5726d897389f"}, + {file = "pydantic_core-2.46.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:337639ba62a11acde6ef3aeb08c8ea755f8ef1fe5e513356c0f36a2b0d7568b0"}, + {file = "pydantic_core-2.46.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:413a717a410d0c817ef5b786a059415550b3794e1d0c2abffd9efb93a3d9f7b4"}, + {file = "pydantic_core-2.46.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1e449def1945a462c464331254e5a44fca7c3b4f9aedf59ec2f50f8066dd8e25"}, + {file = "pydantic_core-2.46.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:a445486499897b88a7d6c310c88ed64dd37b1b59bfd7ae9107490bbb362f47d6"}, + {file = "pydantic_core-2.46.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:2d330aaba8621b1edcec8ae2c4050f63b84ccf6d98723a8f212e9684713abf0e"}, + {file = "pydantic_core-2.46.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b6acfb46a814762367fb7ba0828b0a17d441b92ce249a0e007474c9072662dda"}, + {file = "pydantic_core-2.46.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d0a24b40877af2de4950252be9d21eaf7fb07660f3c2cae1f56c6b599ada5266"}, + {file = "pydantic_core-2.46.5.tar.gz", hash = "sha256:10416c15b8839ecc4ef4d0885da76da6fd0f67333a0eb8aff6d93c4b8f2910fc"}, ] [package.dependencies] @@ -1839,7 +1835,7 @@ version = "2.11.1" description = "Extra Pydantic types." optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "pydantic_extra_types-2.11.1-py3-none-any.whl", hash = "sha256:1722ea2bddae5628ace25f2aa685b69978ef533123e5638cfbddb999e0100ec1"}, {file = "pydantic_extra_types-2.11.1.tar.gz", hash = "sha256:46792d2307383859e923d8fcefa82108b1a141f8a9c0198982b3832ab5ef1049"}, @@ -2131,7 +2127,7 @@ version = "1.0.0" description = "A utility belt for advanced users of python-requests" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["main", "dev"] +groups = ["main"] files = [ {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, @@ -2302,30 +2298,30 @@ files = [ [[package]] name = "ruff" -version = "0.16.3" +version = "0.16.6" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" groups = ["dev"] files = [ - {file = "ruff-0.16.3-py3-none-linux_armv6l.whl", hash = "sha256:0c5710e247a58a4521e66e124ba9a74655b414f61ba3a2e9e3811e11098f48f7"}, - {file = "ruff-0.16.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:fe155130631a2471fd2e14a7a664a4dfbd7194b8229c3d7b2a40b21178639081"}, - {file = "ruff-0.16.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e2ed719e14aa64d895c2ee922594a90a43c861a93f0575a95ff8c47cdbd13eb9"}, - {file = "ruff-0.16.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e0b1da805eb043654645d74d5de1e5ce2edc686e40790d2b86f56d71cc06a84"}, - {file = "ruff-0.16.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a37bdea0bbe21780f590bf437d6412c8c4e1b6cd010f91a65c2c40c5e5f5f870"}, - {file = "ruff-0.16.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09571e6d1288ed9be475207a3ac04ada404f1cd898104be0f6ab8d7df438575b"}, - {file = "ruff-0.16.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c18c5a101eb540010638cc1ff3c84944d3adb3df62b8d98ca8f22ba484d3413"}, - {file = "ruff-0.16.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8457c44f15033c85ddbb77b15d451df9e24e4bd03b628396dd3610cedc3b8f82"}, - {file = "ruff-0.16.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:294b95c4ae0cda9388525c2047778aa758d6b8d4bb876fd4e9eaa3ebc92343eb"}, - {file = "ruff-0.16.3-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:3d0c7c40c87c2a820509c31ba007968da6e1306468c067b2d82fbfdbcd0e8474"}, - {file = "ruff-0.16.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:9f738c0fdfa8eed0b2ce7fb27ee7258208a92a68d7949e62aa15164bc7b389da"}, - {file = "ruff-0.16.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:fb785f0be25abe69d320415cd4f833b59e17ba7613d9ba6a958023b6bceb0a50"}, - {file = "ruff-0.16.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c5536e3acfbf9563085aa2be7b13c629c3077e902afc5b941ac44024dbb9f506"}, - {file = "ruff-0.16.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a2d85c02f9b8e165d85e6779184d38c4132de12603dab59c51c28e22584f9e4d"}, - {file = "ruff-0.16.3-py3-none-win32.whl", hash = "sha256:388cdf2166642bd9b13d52b5932d3170f34f8abed7e8d9a855f1d84b83645a0a"}, - {file = "ruff-0.16.3-py3-none-win_amd64.whl", hash = "sha256:e80a7d69ca2a6d1c4d352ec91458cdca6e56c83cdbcabd93e4abe1e53591d948"}, - {file = "ruff-0.16.3-py3-none-win_arm64.whl", hash = "sha256:b8ca152da82c1acc1fa8d5874b15951935f0eef46f10e6954c83859011b6178a"}, - {file = "ruff-0.16.3.tar.gz", hash = "sha256:e76d33a347661a84b5be6d043d0347fdc745dfdcf825a8f4fed64b5e26eebdf2"}, + {file = "ruff-0.16.6-py3-none-linux_armv6l.whl", hash = "sha256:61c368c26bf8e973e5ab14a2772de587bc068ea3f9a277f673380749b4898fb8"}, + {file = "ruff-0.16.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ecf4f068e2e123e43a26e9db4e19524cc56563912404e83bbfca375757e45a32"}, + {file = "ruff-0.16.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:99b62ea33baf130f50368798d841f0d95527b6d817bf31817b65dd058f1d314c"}, + {file = "ruff-0.16.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fbf89013f2bb3f6835a6038ff658dc8a1b38c98dc8e724b964168ad4e881876"}, + {file = "ruff-0.16.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:56a67065e22efa6bc4d498299d3bb06c0c90aace8fac2068b5a12f9dc4d8d51d"}, + {file = "ruff-0.16.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e25cc89174874b176a157e4428d66761c2c0c006654419bf384f967f361ff1b1"}, + {file = "ruff-0.16.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0700580ed5303723cb3c11c2f1d2a8913ce77b7ea86646dddb887f5417a9ba70"}, + {file = "ruff-0.16.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15f1d0b6e165a6e56567befb6629f8209271311d990bae0f37e6d065035ef5f3"}, + {file = "ruff-0.16.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d72c591a96986ee4268860e2b7235082129ca5e4cb9cbba653a4b57c11893757"}, + {file = "ruff-0.16.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:65a006baa18f33324325814c864daef03541d51564b98c517610ea756ab7003e"}, + {file = "ruff-0.16.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:cd02a7bf1a21a8735228a3e8c95a9dc5cf86bd2a52194f4aaae2a5755b4de0f4"}, + {file = "ruff-0.16.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:31b36f1e5ad85e0737f09d2be4e512e2e283583c14015da3b9dc07359ac0fc88"}, + {file = "ruff-0.16.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:61029b4ab4aa723fd3064fab96b1d814492596bf0c792679fffcbde1e1679953"}, + {file = "ruff-0.16.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9ac8998457832c2061709d900856b7ad271dace0cb41f346588d540162bfa718"}, + {file = "ruff-0.16.6-py3-none-win32.whl", hash = "sha256:0b87d9d16fcb63e8018423ca1d50b7260f15cb2da33e30db4baad4183a948c25"}, + {file = "ruff-0.16.6-py3-none-win_amd64.whl", hash = "sha256:10d21c51c3495d8eaea7b703a16592117ea6eb1d649e36335aa965ff1173eb39"}, + {file = "ruff-0.16.6-py3-none-win_arm64.whl", hash = "sha256:7a976c79b958f94e50a022a19f0f8c87387448020935ec14fc74331bd0a7f2c5"}, + {file = "ruff-0.16.6.tar.gz", hash = "sha256:dcf8a73d2ff77e99dde91244b4da16feba7f14e6beeb4015dee7c5a909e99050"}, ] [[package]] @@ -2334,7 +2330,7 @@ version = "3.0.4" description = "Python helper for Semantic Versioning (https://semver.org)" optional = false python-versions = ">=3.7" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "semver-3.0.4-py3-none-any.whl", hash = "sha256:9c824d87ba7f7ab4a1890799cec8596f15c1241cb473404ea1cb0c55e4b04746"}, {file = "semver-3.0.4.tar.gz", hash = "sha256:afc7d8c584a5ed0a11033af086e8af226a9c0b206f313e0301f8dd7b6b589602"}, @@ -2406,7 +2402,7 @@ version = "1.3.1" description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, @@ -2448,17 +2444,6 @@ typing-extensions = ">=4.4.0,<5.0.0" [package.extras] syntax = ["tree-sitter (>=0.25.0) ; python_version >= \"3.10\"", "tree-sitter-bash (>=0.23.0) ; python_version >= \"3.10\"", "tree-sitter-css (>=0.23.0) ; python_version >= \"3.10\"", "tree-sitter-go (>=0.23.0) ; python_version >= \"3.10\"", "tree-sitter-html (>=0.23.0) ; python_version >= \"3.10\"", "tree-sitter-java (>=0.23.0) ; python_version >= \"3.10\"", "tree-sitter-javascript (>=0.23.0) ; python_version >= \"3.10\"", "tree-sitter-json (>=0.24.0) ; python_version >= \"3.10\"", "tree-sitter-markdown (>=0.3.0) ; python_version >= \"3.10\"", "tree-sitter-python (>=0.23.0) ; python_version >= \"3.10\"", "tree-sitter-regex (>=0.24.0) ; python_version >= \"3.10\"", "tree-sitter-rust (>=0.23.0) ; python_version >= \"3.10\"", "tree-sitter-sql (>=0.3.11) ; python_version >= \"3.10\"", "tree-sitter-toml (>=0.6.0) ; python_version >= \"3.10\"", "tree-sitter-xml (>=0.7.0) ; python_version >= \"3.10\"", "tree-sitter-yaml (>=0.6.0) ; python_version >= \"3.10\""] -[[package]] -name = "timeago" -version = "1.0.16" -description = "A very simple python library, used to format datetime with `*** time ago` statement. eg: \"3 hours ago\"." -optional = false -python-versions = "*" -groups = ["dev"] -files = [ - {file = "timeago-1.0.16-py3-none-any.whl", hash = "sha256:9b8cb2e3102b329f35a04aa4531982d867b093b19481cfbb1dac7845fa2f79b0"}, -] - [[package]] name = "tomli" version = "2.4.1" @@ -2562,7 +2547,7 @@ version = "0.4.4" description = "Runtime typing introspection tools" optional = false python-versions = ">=3.10" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "typing_inspection-0.4.4-py3-none-any.whl", hash = "sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147"}, {file = "typing_inspection-0.4.4.tar.gz", hash = "sha256:547274fa6b0a561ccf549cc9524b999a578e737d015d8709d021f9d0d13bea47"}, @@ -2571,22 +2556,6 @@ files = [ [package.dependencies] typing-extensions = ">=4.15.0" -[[package]] -name = "uc-micro-py" -version = "2.0.0" -description = "Micro subset of unicode data files for linkify-it-py projects." -optional = false -python-versions = ">=3.10" -groups = ["dev"] -markers = "platform_system != \"Windows\"" -files = [ - {file = "uc_micro_py-2.0.0-py3-none-any.whl", hash = "sha256:3603a3859af53e5a39bc7677713c78ea6589ff188d70f4fee165db88e22b242c"}, - {file = "uc_micro_py-2.0.0.tar.gz", hash = "sha256:c53691e495c8db60e16ffc4861a35469b0ba0821fe409a8a7a0a71864d33a811"}, -] - -[package.extras] -test = ["coverage", "pytest", "pytest-cov"] - [[package]] name = "urllib3" version = "2.7.0" @@ -2607,102 +2576,113 @@ zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] [[package]] name = "wrapt" -version = "2.3.0" +version = "2.4.0" description = "Module for decorators, wrappers and monkey patching." optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "wrapt-2.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0bb2797048db0956348cb3058c33bc4184614f13231389cfbccc16a5d32780a7"}, - {file = "wrapt-2.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce9f398f868d2b3b27aa2ea4de79645ef9077aeeac8dfc2814b0d542c6a2b87f"}, - {file = "wrapt-2.3.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ad71df7a04dd3497e9302e81f4a7c91bd401ea0e15a9df9029527900f94bee43"}, - {file = "wrapt-2.3.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fc82c2ccc8e234c844f5303d9f2984b346dcdd53e94823ce8420d2c75b4b9023"}, - {file = "wrapt-2.3.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a6e19531ae33c508cea7d84a7edfda01fa86e51b8d1a93a77712c55e6e469152"}, - {file = "wrapt-2.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:df4ce31150bcd5d9f36f816aac3010ab4f4bf8672ac1d3b0ac7d539ec61c7c02"}, - {file = "wrapt-2.3.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:e2e692bc0d63f881cf7006730a56bd4e0c2fab5dc318466942805d692b166276"}, - {file = "wrapt-2.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c8388ba7faf5dbf9ee106bb70d66f257629b1bd98091123e19e8a4553a319199"}, - {file = "wrapt-2.3.0-cp310-cp310-win32.whl", hash = "sha256:e045ff75d7d94900fc32896ed93c45ce2d2cac28c9dead582ff9a5a49d446e35"}, - {file = "wrapt-2.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:b4fc96b159af0a3e0faa72475a69d66292bea72a5bed1e1aca1bffbddc3cb2b0"}, - {file = "wrapt-2.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:1236fa25173ca964c97422470482e9011b9e3c7ed0d75798b40b3da3b0e0e760"}, - {file = "wrapt-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5ab559e1b2551d23d54db2a0001c6d73bad022a254639561c5f6c382a9d6c2fe"}, - {file = "wrapt-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bff9a671bc00709cab5a7f745c592b5671873449db0ee2a569af994f16b29a4d"}, - {file = "wrapt-2.3.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fc648a335d7e01adb3640b25f02fd0ea05886cf04d0af7f4ee902bc7b5e466e8"}, - {file = "wrapt-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d0077f3d65541925fa83002f967b22ad6550d24813ac64cb905f717194128d9c"}, - {file = "wrapt-2.3.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9790ea25190a4e0fe4cdf4eeb868e9d75f8a024a70a5b6bf9c348a3a2b72e731"}, - {file = "wrapt-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:816877aa749253149f9ecfd2635d4d948ecfa338e1a0311d187b1acb1bb8a3eb"}, - {file = "wrapt-2.3.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3d1c2c1b808600d2ea808e6360910a60ed5f409a4011655e10f9164ba0a414a6"}, - {file = "wrapt-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5ba1e5e08ddc46130e9682b2c249f2d1dd39bda9106ed4bd401b7519f18f41bd"}, - {file = "wrapt-2.3.0-cp311-cp311-win32.whl", hash = "sha256:45c9279b373d15649dfa2c2077cb3408ea1a6d3125afbdab9d6b809a66f68e14"}, - {file = "wrapt-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:195b1842b4122fb54e3cd3dd5b2b4aa49302a5a61da901df0481f5c97aedde84"}, - {file = "wrapt-2.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:6db604ef0c67bdb2042ecdfd7b7f037cf09733557ca42360d1018285634f7b98"}, - {file = "wrapt-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0a45ffae742ce91a16e11cb6c7cd71e7f9994f3cbd283b962ab093f5c6dcf525"}, - {file = "wrapt-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:69e477046f2237ef0bc6547544ee73008dc764ca26eff44f09e976d221b34d5d"}, - {file = "wrapt-2.3.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d221a6e6ddd302b8397433184e96b59f259f50024b854db1c411a881586b6b8"}, - {file = "wrapt-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:392158c9a7f2ab1b8699418bfc0fe6f83548788c418b27d7bf2019ad3405cebb"}, - {file = "wrapt-2.3.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e5301c35cf75655eb33498f2bd6ae8703ca19940e3167dc9cdf740c712a39c60"}, - {file = "wrapt-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:418f54bb09d1762db02c7009b4051149893af3153a87f92d70356703c11eea02"}, - {file = "wrapt-2.3.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:1598becd30f8f2777d18564064eb4f4dbe1ab0e05a8f09786d0ef505ac782bf3"}, - {file = "wrapt-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3da470536bf9645143323dd41b32db55c6f4304ad382094c1a1da8a92061e10d"}, - {file = "wrapt-2.3.0-cp312-cp312-win32.whl", hash = "sha256:fb8e2e6704a1e0b1b989546c69e2688371ef4a07fa5f61bde3eb6211186f5ac1"}, - {file = "wrapt-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:cdc021cb0b62471d6aac7f2bd92f3b4658073775f9ee7fcd325c511129e7bcc8"}, - {file = "wrapt-2.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:67bfe2485f50368c3fcd2275fc1fd100e350d601e0058921a7c82678a465aeab"}, - {file = "wrapt-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d3fb71e65b001adfc42684522eeccd9c21d8ba679945abc993439567b66e59f"}, - {file = "wrapt-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:51a7a4181c1295774812271fbcd7c909df372bc25579d4ed9eb875caaf0ae86f"}, - {file = "wrapt-2.3.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9045917809c63fdf7abe3a2ceaed3d670b8ee4500ddd9291192d30aeb34467c5"}, - {file = "wrapt-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:54ca1d5573f69b5fe1d74f1f65799c68015e82f685efec9fd8cfa40a094c44d0"}, - {file = "wrapt-2.3.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:242b60c21e30866e6a2fa606c612b47c553fa60c0eaeeeb7797fb842ac0ce609"}, - {file = "wrapt-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3f3d7ec0a51fbfe00d3aef047641ff2c58b25565b4717fc1f90e050be01cba8"}, - {file = "wrapt-2.3.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:261f53870cd4fb2bf38f9f972c56c728fd224cb7c65721307de59d9e7e6741ae"}, - {file = "wrapt-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8159ec0b0cb7608175eb150de94c19e34f4d47ac655f5ca9baf45df6b688ffd3"}, - {file = "wrapt-2.3.0-cp313-cp313-win32.whl", hash = "sha256:10461884b3014fbfc8eb7d09a93c5f246363e6711d9d881f95eb8c27fdef049f"}, - {file = "wrapt-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:ac870cc97b73bb00ac353329e9559a4bebc47c4c86792ed9b23b58c15b6ad838"}, - {file = "wrapt-2.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:a65e8db2b4e90c2e7ade931086351c98ef420bf7a94ee08c95ac8a3cbbc43579"}, - {file = "wrapt-2.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:fd1f2f557dd3491fe75905e578f4db967393d40d1a8f468edc4d40ac7f2d5944"}, - {file = "wrapt-2.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9f5d2aec29dfc76c37e23897dee92766a3fd4f3bff3ae7fc9c6b4bf37d8c1360"}, - {file = "wrapt-2.3.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:646d20d413ffcd1b0a2f700076e2d0252d872dcb7754860a73e45a59ea883614"}, - {file = "wrapt-2.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:379f670f45b7bb8993edd9f6fc36c6cc65edb81cffa0b504be34acb0303fff0a"}, - {file = "wrapt-2.3.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6208f302f110295d64b22a7ac96500c791bf492dce4366e622e4912b077c9687"}, - {file = "wrapt-2.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ed635a9ca4f3a5a2b900c10c69e823373bc00ebc114b459383596d3487da3570"}, - {file = "wrapt-2.3.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:e3b9eaa742ae7a0aaaaad4ca4b69469d757af2d6e6663ef1dadc47adec0aeb41"}, - {file = "wrapt-2.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d0f7284f88f4833705132d06d3b425a43095c2cbd07c58166aac3ab646ba12a4"}, - {file = "wrapt-2.3.0-cp313-cp313t-win32.whl", hash = "sha256:7ebb274aba688b043429eb1500ff8a76ce0cb8ac0812ca3e301f06247b8722b3"}, - {file = "wrapt-2.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:c4bded758ad6f03b965830944a2f0bc5b2eb3767fe5a7310134315d1a6610e98"}, - {file = "wrapt-2.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:d2cc64539da63e39ffb9c7ede849b6e8ddaaf7b3876b5cfb04efd85a5f3f4eb6"}, - {file = "wrapt-2.3.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ea52a0d0f08c584943d5764be0e84efa912c8da23c23e1e285ff2f5641c18fcc"}, - {file = "wrapt-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd85b0aa88efdb189d6ae2f35f4526943a8f091c38599c9c31478241c819e6a1"}, - {file = "wrapt-2.3.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:141ed6211286a9660d8d6702de598b43f0934b4f0eda16393f100a80f501d945"}, - {file = "wrapt-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e49885a62ec4ee854d1b9e6371fda6afd219917225752abf729a3f36d4df9a5"}, - {file = "wrapt-2.3.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d6159c9b2fefec02314e1332dbbbfaf960e369dfd26bcf7f8b258b5732065b3"}, - {file = "wrapt-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:24da48596326ef8e448cfa837b454f638713d3531262375f00e5a9681682fc07"}, - {file = "wrapt-2.3.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:cd3a2edf0427013736b8127955cec62608c56e53ea47e82812ea32059cda407f"}, - {file = "wrapt-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4fa0df3bff4e7ce45759f33fd39335fe2f60477bb9ecf7b8aa41e7d07ee36a23"}, - {file = "wrapt-2.3.0-cp314-cp314-win32.whl", hash = "sha256:2935d5454b3f179a29b12cf390ee47246740ba2c3a7545b1b46ba31a5f2a4a0b"}, - {file = "wrapt-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:cc2cea812e5cb179a796b766747e7d3b21088760d8deb95676d482b8c8e6fa7d"}, - {file = "wrapt-2.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:22cc5c0a717bd4da87018ae0bffd4c19c6fb679d3ff357216ba566ab26c76cab"}, - {file = "wrapt-2.3.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:a6b5984cd65dd639546f0eb4b8eacf1c31cb2fe9fb5c27bffe240987cdb2cf84"}, - {file = "wrapt-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c88abcf53daef80e01a75c7530e727fa6e2c1888fe83e3dcdba4c96216a1f5c7"}, - {file = "wrapt-2.3.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:85de890ff968196e92dd1ae73a9fb8970495e7650a457b1c9ef0ac3dd550bce2"}, - {file = "wrapt-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:50f416b74d092bb9f41b424e90dd457f365f7ba4b11de62a23679769a21bd85c"}, - {file = "wrapt-2.3.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:39febbee6d77301d31da6996b152ce52452da7c7ef72aba10c2fa976dff9c295"}, - {file = "wrapt-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:93513bec052c6cd987f9f580c3df068c8bc4ebae6543736be3ca7ec5959cafcd"}, - {file = "wrapt-2.3.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:729126e667da34d251b8ebf8a45ef0c5ddadc21542b3d6e1abf4259ece6508df"}, - {file = "wrapt-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:626b69db2021aa01671ec7bbc9740e558522bd44c18cf2ce69bf3d666a014109"}, - {file = "wrapt-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:629d73378082c00a8173031f9fb30a3ac6abbc894a5bfdfae71fabc60642d501"}, - {file = "wrapt-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:42869085687f0aefd57c0f636c3f9354f8ffb321a8ba9cb52d19beb796e561c5"}, - {file = "wrapt-2.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:b1e5aa486e269b00ed35e64771c7d0ab8096cfd2643405ca8cd60ebedc099a51"}, - {file = "wrapt-2.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c3b476ae63b4a3b4da681aafcb25ff3542d289fbda8b5da7caf76aaffafafdbb"}, - {file = "wrapt-2.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:932dced0a7b2950ed58a3325536a1dcb7b58e7330af54e8552d2e566b5328b99"}, - {file = "wrapt-2.3.0-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0db083387d6e75ec0be8173ecbf0e811cf60bae1cc75a815feb104167ea10d4d"}, - {file = "wrapt-2.3.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:abc71504669d126d91f89fc0e388c6295d8fbd2439be884f175133fda8aa403c"}, - {file = "wrapt-2.3.0-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b767a9566f165dd14decf8f4194c6bb0ce3a8420cec213824e05a99400c9260a"}, - {file = "wrapt-2.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:73d0b10b64620a2cf4bc3d31775c4d9527e309a5549e4379e3bf71e8d2dc193e"}, - {file = "wrapt-2.3.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:e31734c5077f29f892b2565eee5106d610278151ad49fc6a9d69a647cd5730e2"}, - {file = "wrapt-2.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:628f3ba8ec793a5b10a6cd8c6c6b7b55eb552abd1f3bd301336acb74c7a82dfe"}, - {file = "wrapt-2.3.0-cp39-cp39-win32.whl", hash = "sha256:3873c3c5ca9f4ef91f693602eca19d1f1e7c410338df82a4ff11d826b5896a8f"}, - {file = "wrapt-2.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:8f8a1c6472675956cece9a8f403f43c3594f1681319eed2dd56f60877397c636"}, - {file = "wrapt-2.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:c8858d8ff9822a081e3cc49ae1b3b22f0f789c14001cdac8f94564010d9c9d66"}, - {file = "wrapt-2.3.0-py3-none-any.whl", hash = "sha256:d8c7ed08477429752b8c44991f40ad7838b18332a160698740a6bfbc10d998a2"}, - {file = "wrapt-2.3.0.tar.gz", hash = "sha256:681a2d0eefd721998f90642762b8e75c2159ec531b20ad5e437245ea7b06a107"}, + {file = "wrapt-2.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:643e45aa88698c8aae938c50e61940985d4ab9e53ea666d3e8e4eb86a4820d0f"}, + {file = "wrapt-2.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4597d19904b4aa97331d8bb651ac626d9397727e717942cf11bd7699ff97aa45"}, + {file = "wrapt-2.4.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:635cc171ddfd72edff10e295a02daa65edaa1c0ba619ad11eeed15cd2258c5df"}, + {file = "wrapt-2.4.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:932a8892265df7b71257c30e5752635bc1f06a8c4e264024ff031bdf9bb10918"}, + {file = "wrapt-2.4.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5b8fff692f74782de89ba9d7b526a7cc398569b6a988ddc848159cc033c86237"}, + {file = "wrapt-2.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a27d9653e0f88aa06598954337a545fc3f75bc811df897157a8614846d18d9c"}, + {file = "wrapt-2.4.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:9c884240e7415d3a384e70a15ceea0e884cc9289bcc254afd6412d4e7cf99c47"}, + {file = "wrapt-2.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37ba372e9ae71ec43e165b5db05f52e71f7c07dafb9d6a254ef7128112dce751"}, + {file = "wrapt-2.4.0-cp310-cp310-win32.whl", hash = "sha256:07daab5babb7edaf89413f5c8bd638474540fb2643b5dfb685bdc0680c96803a"}, + {file = "wrapt-2.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ef9797bf7c6f9ad9d294538c4f9a64ef3dbbadb63590a9a067393fd49ba28b0f"}, + {file = "wrapt-2.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:11ccb5f3de2047ef91408464abdc04682e40e7d7bc9614885d2abcaa7e2ef149"}, + {file = "wrapt-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a67ec80d15ac199d4a9a04a33f3039a1c219c9bf1c07b1b0422497613f167fb9"}, + {file = "wrapt-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fc1b2cebd6d8db9b4ac0adc817c08b4901922e85604ae2a69aecb5217b2c09d8"}, + {file = "wrapt-2.4.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e52c6a5be3284719e53b629ccfa565c146e604e861de35e861c94f7622806eb5"}, + {file = "wrapt-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9905bceb7b2833559518574ad6259d2ec9ffd111a0aa330ca685db74478e1ae3"}, + {file = "wrapt-2.4.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:abc347e92f9202c8ac1d5c1626a800fd5e56e13433f0651b26dddda5b421ac79"}, + {file = "wrapt-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:52f01626f1d2bc54585954cd8b4931f81003b0ac8dad61c741f43014bc9a0f0b"}, + {file = "wrapt-2.4.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:811a36628d8b76724b980d508d576e5c5ecae1073b6ec4b4eb21646921906fe6"}, + {file = "wrapt-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b33df90f3d1e5b1c8811830b11a3e718b4f3a2823b748fa9be1688cb82b193f1"}, + {file = "wrapt-2.4.0-cp311-cp311-win32.whl", hash = "sha256:be535bdfbedda84cb8ebc6a80955dfd03d46840c13470486bd038f089e38b172"}, + {file = "wrapt-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:a1117c63a39ba4d1b884e658089e512412d5174217ea1b4fe570977e42a5b129"}, + {file = "wrapt-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:637fd6a18bb668a0c27b4767dcbc2fa93119c90da735bd2669fdde2d7b59fab3"}, + {file = "wrapt-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ef4e2d6e399ce6eecc80179a6b9ef6544f121288f95fc132bc36c9d9503903af"}, + {file = "wrapt-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b9b32d5e4f0a179cef5075cc79b79d6d3482c44c434c12969e48c6719e06d95"}, + {file = "wrapt-2.4.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d7dbbdbfdacb85c2d962fa52db791c77943fd777d600d74c95af2d53b32f5a94"}, + {file = "wrapt-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39cd68df4dff79f5336f9c745c06259d204bcb42d504040c9c91eac9e2abb39c"}, + {file = "wrapt-2.4.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2a9f1a2f75bb95257cc5744e255e10a5a86e923f328b40ad3dbf9d8d03430013"}, + {file = "wrapt-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8763ad01e3725b7751a4575f38bbcc19c0aa0822fec91c5c5bd21ce3ce7e1d2b"}, + {file = "wrapt-2.4.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9125c6dbe8b88c00dd8ef4fc1e55757e8eb4720b6b2b2cc610a45bd32bd28c57"}, + {file = "wrapt-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:28f5de1526831b8f173889a436e289fe181ede8c66c9feb669d1aca8fd602eaf"}, + {file = "wrapt-2.4.0-cp312-cp312-win32.whl", hash = "sha256:a9ca1cdb3f7facb4990c7739ea5afbaceeb6728d066feedde03a4cfe83b29b03"}, + {file = "wrapt-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:8b464316489fb2fca0669ea0f8f07290054a0f26fc72982d3e4cf95469628ba9"}, + {file = "wrapt-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:db1285071ea09a7767fac608e7b5c7b03c09833b06186875a359905fbc659d29"}, + {file = "wrapt-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5c5c4c728cd22a36e4b8bb5df4a7d3bccaa865d27725b36eeb3b6f18fb2e1bc2"}, + {file = "wrapt-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7de5b8d94417e55c02be50cc226e0ae1209bbc73813bf691dff3979c94438115"}, + {file = "wrapt-2.4.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6436e2bda993a3eb69a1b317fc831c8ebcafb5704c390859ebd49f81218c4bbb"}, + {file = "wrapt-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e084558fbd112d2e1e34b0f5c71e45a3405bdad51a17150368a959bcf6697964"}, + {file = "wrapt-2.4.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e78c947e18fadfd690c9420c30a96d221feeb93fc8f1cc00509b370ac16c3114"}, + {file = "wrapt-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:08d8378c4514ac8dcc0ace76044cf87a873e6a52b5e6109834c8fb9037f4441b"}, + {file = "wrapt-2.4.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:93180c2199784dd6a1075b33f9ed636bd0966821edbece6b3d5379b1c4f0bb7d"}, + {file = "wrapt-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3d5e5eb76fb87e62752af751d2dcd9d1cd986b12037d2e1363d109ba716029e8"}, + {file = "wrapt-2.4.0-cp313-cp313-win32.whl", hash = "sha256:49bb5a572469e0e18163a8ec2aa972135a0929899ecbe627665f274506e1b5b4"}, + {file = "wrapt-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:b1737f46b1e4a81eb93500a7f2854319e1c7a86e8863fb050b7b4daadd5a4178"}, + {file = "wrapt-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:f1e9e088094f4895f84ab043e7d59401df137d663efbf1e80c82144882960830"}, + {file = "wrapt-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:788e473d1a6786d29d577b1e2bd95e214c09cdafde84907c522c31069c9acfac"}, + {file = "wrapt-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:947bd4b3438167b3638bf5477cb83a068a586ffb6d331ac427f39839c2b93b3c"}, + {file = "wrapt-2.4.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3a69161cae7f0dca44c89c1d14146b4a0508a0c3cad98b3f2db1f4e9016c94ba"}, + {file = "wrapt-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0536f5d85ff6a157ebe7e0fe08c5479943742cf1ce59569075a66159efcbc495"}, + {file = "wrapt-2.4.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5f041ed6a4d571010944bd6cfad9072db463e1851877b6d3227467a44af37456"}, + {file = "wrapt-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f7fed45dbadf5d98a52bfff9624d3cca00affeb9543d493c9632b7a53cdd35c9"}, + {file = "wrapt-2.4.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5cc2e7c7b6032e11a2b367a9baadaf0c5241feff2d8205260d87f1aa6dbdf84b"}, + {file = "wrapt-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:72826910a1cf5a081234720fd43011304b899acfee219af49148155b4d795533"}, + {file = "wrapt-2.4.0-cp314-cp314-win32.whl", hash = "sha256:0eca69c9e93518240abe8801fb9b2726116a6e48172e4564c2651a2e14521747"}, + {file = "wrapt-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:63b94f401d7ae3a9a3027472fd3a3ff38afd2ed293b2f0b3b84a6d133a9f99a3"}, + {file = "wrapt-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:6b3e082d43f592fcd381aee46354a11ce887a813ce5bbcedd9766fd681723c09"}, + {file = "wrapt-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:09064c7be688c38c3ff125ce86bc26b69b5d78dd56062c3ddd9c814b2a25f1e1"}, + {file = "wrapt-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4f8ddff4bbb75916be36da5169b8b9d475b59a1bd24acdb7551bb2c71be9aaac"}, + {file = "wrapt-2.4.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e9f8017443595870aa31f46125553a5c55ce95a26a267b96261baee6ba566d83"}, + {file = "wrapt-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:328eb2d978ca3a6ae25f8d8fe560bf8f4bc9778b5932e7b142664eef05b92e8f"}, + {file = "wrapt-2.4.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7a057d376d994da6bd1bbf955ecfda699aa7353826f98847f5605e1801abdfd4"}, + {file = "wrapt-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3367a5212212c9393e0d3ca6ae029b3a8fa40c5896e4a985d43fe8a4b8322f0d"}, + {file = "wrapt-2.4.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c4fca1e63af6675af3df7cdfcd5a0c878b5e655c7e48611ced9dc8d62183a11d"}, + {file = "wrapt-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:694005fdc3002ade0f21641408c588028abde03c85961f3ba7727d8bead3ed6b"}, + {file = "wrapt-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:332d9bad7e9b718974bb2a576504c4956f45b4a0fcd7b3bb7827279167550464"}, + {file = "wrapt-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6d57264c9dfcf37d2bf0b0fbec68d0f6184fc5617267619ada04d03e8b0231f3"}, + {file = "wrapt-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:f43af38a642c3d6062e9740d8f5cc0feb5dbe0da516702df892147393b8cb14d"}, + {file = "wrapt-2.4.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:430fde1a116df3ceb5c29035de1da6609b70e680d9b8ce3ee624422f3fe0978c"}, + {file = "wrapt-2.4.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:7d28f8f35a02d49f75f57fa4e755db4ba33f65841c0de64cd65b253916f5bf06"}, + {file = "wrapt-2.4.0-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:efd9a4be6785295e471f71efdf5682bd11d5b822b9665e6e1b4844917cf2f7ac"}, + {file = "wrapt-2.4.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:75529a2fb569a671cf162f762c1b576f569f571b55ec7f3481258ca842ba507f"}, + {file = "wrapt-2.4.0-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66e7512c0d324cc37bba1def2be1fc365cbb685d3aa393a8f6f4d2d00202881d"}, + {file = "wrapt-2.4.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:5f3bdfc35c83b562fcaebc0f24593045e5ed9f3b633adafd35222718a0ec38fa"}, + {file = "wrapt-2.4.0-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:d5f45bead708e2c0014be5e98531ce7202916b098a208c7be83c6ceb0a2559fa"}, + {file = "wrapt-2.4.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:d294576fddac636589e4deccfe782e8f429da10f167c1985c4d51071de3672b7"}, + {file = "wrapt-2.4.0-cp315-cp315-win32.whl", hash = "sha256:0191d717dfbb8e519e7bfd4775e5b9bd57e359b3a09ab5db1ea47f6025b4d845"}, + {file = "wrapt-2.4.0-cp315-cp315-win_amd64.whl", hash = "sha256:e8df31a126a0a247c1aa379e30873839de03912dea09ca360c680f3625d815df"}, + {file = "wrapt-2.4.0-cp315-cp315-win_arm64.whl", hash = "sha256:e9e7e94472f0e3f1447caf27e1939eb384d0e87972a35a05f5c2e0968e9c01af"}, + {file = "wrapt-2.4.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:8828369b7d3e93c547cc8ad931b5a57b4e8d174035c82762fb1091e7d05ac9f5"}, + {file = "wrapt-2.4.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:413e757dce7a43fcda8bb8441994b1127492ffac6a5803af777d44516df8c6e2"}, + {file = "wrapt-2.4.0-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:75944792cf6b99262d649d55710bf5901f7013fbb212c7a1d736b97a20517607"}, + {file = "wrapt-2.4.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:648d1d4f94e8a0a1656675c755f40d2f0ee5fe92c449ab45326f4ecc2738cbe8"}, + {file = "wrapt-2.4.0-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a112a1bfdd2621e4344cb0a32dbaab80636b32dac1b055d03fbb2a67d806d1db"}, + {file = "wrapt-2.4.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:0972cd025f4c86fa2d8abd953d9f875779935343af58b4ce019ff89573fc65bd"}, + {file = "wrapt-2.4.0-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:c246aaed719dcdb62eeb7b8d9306a6237777226ef3baad35919c4ae134c91ce7"}, + {file = "wrapt-2.4.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:1656de3835f760781c9b974bce07d8c04edb9c9ad7ad67264aee69cd68a1db09"}, + {file = "wrapt-2.4.0-cp315-cp315t-win32.whl", hash = "sha256:d8e6e1e5dc684dfce7c33fc8b67a08ba2af94f3a45cfc70d5c1d6a839d2caf97"}, + {file = "wrapt-2.4.0-cp315-cp315t-win_amd64.whl", hash = "sha256:85ed3c67fd39e8d9a36c224758cb6f2f4eb277d07ea677930caa0008c18ec002"}, + {file = "wrapt-2.4.0-cp315-cp315t-win_arm64.whl", hash = "sha256:36b56a4fba13b34ed8ff307557325fff215de0a58b5dbaef2c50e4d8aa39dbd1"}, + {file = "wrapt-2.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c4b42c92df24a986da7e2b5b44fb142504d858c54a276f9f366983ca4482dfda"}, + {file = "wrapt-2.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:15bb88c0a6c6312244917bb0a094368746fefb92663209363e16f20972a57b34"}, + {file = "wrapt-2.4.0-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2dc0f6412aaf5fc7e6a3abf119b7c671dbd026303daccac20112c046a48b68b9"}, + {file = "wrapt-2.4.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:717dd7ef439863933664951f89902b7e0ee3652293543cb9917c2e16bbde1949"}, + {file = "wrapt-2.4.0-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:174f3576dacf55c8a7c21719d4b7c8088efb991888db5728cfc891b80b28853f"}, + {file = "wrapt-2.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f53837b56ca834f381d300621f8c9525b9da517331ce0f4b805ed08f63bedcd7"}, + {file = "wrapt-2.4.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:4a1d591386ec4aa99780f672232868620f4f3b63401e2ceb529762580ef8c54e"}, + {file = "wrapt-2.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0b8851a54b137eec9a8480d73cc1a309613e6a465d6f157300f1eb6b5b7c0505"}, + {file = "wrapt-2.4.0-cp39-cp39-win32.whl", hash = "sha256:325c24cfddb46f93c931cf37fa3a9929ac94e70a5627efccd51283f9fd69c6db"}, + {file = "wrapt-2.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:d7c496a966d70f8caf215faddc00de9931a5bf652664221b785a73b20229a696"}, + {file = "wrapt-2.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:6a48bc764deb585d3b8862544ca12df417109984e018767b4ac9aa46bbb55ccd"}, + {file = "wrapt-2.4.0-py3-none-any.whl", hash = "sha256:18aabd9301d06026f5900538051773d6f87f65ae02cdc60de482df978513dc0a"}, + {file = "wrapt-2.4.0.tar.gz", hash = "sha256:7082fc1f94b020ac275870c4af71b09cff22876fe6e9c4c0ad01ea21d217b288"}, ] [package.extras] @@ -2714,7 +2694,7 @@ version = "6.0.2" description = "Variables defined by the XDG Base Directory Specification" optional = false python-versions = "<4.0,>=3.10" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "xdg_base_dirs-6.0.2-py3-none-any.whl", hash = "sha256:3c01d1b758ed4ace150ac960ac0bd13ce4542b9e2cdf01312dcda5012cfebabe"}, {file = "xdg_base_dirs-6.0.2.tar.gz", hash = "sha256:950504e14d27cf3c9cb37744680a43bf0ac42efefc4ef4acf98dc736cab2bced"}, @@ -2723,4 +2703,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = "^3.13" -content-hash = "c8510d9297dcf5eaa563b07622fffc4475f397ae217159d10d326e4ab3bfd424" +content-hash = "f238d7b09c5f6210c05e94136bed8de3930f7c037b03ce37d2a93c525e74313a" diff --git a/pyproject.toml b/pyproject.toml index b865ad8..18f4d42 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ cmem-client = "^1.0.0" httpx = "^0.27.0" [tool.poetry.dependencies.cmem-plugin-base] -version = "^4.19.0" +version = "^4.20.0" allow-prereleases = false [tool.poetry.group.dev.dependencies.cmem-cmemc] @@ -58,9 +58,6 @@ bump = true warn_return_any = true ignore_missing_imports = true -[tool.pytest.ini_options] -addopts = "" - [tool.coverage.report] exclude_also = [ "def __repr__", @@ -85,6 +82,7 @@ select = ["ALL"] ignore = [ "ANN204", # Missing return type annotation for special method `__init__` "COM812", # missing-trailing-comma + "CPY001", # Missing copyright notice "D107", # Missing docstring in __init__ "D203", # [*] 1 blank line required before class docstring "D211", # No blank lines allowed before class docstring @@ -97,7 +95,11 @@ ignore = [ "G004", # Logging statement uses f-string "ISC001", # single-line-implicit-string-concatenation "PD", # opinionated linting for pandas code - "S101", # use of assert detected "TRY003", # Avoid specifying long messages outside the exception class - "CPY001", # Missing copyright notice +] + +[tool.ruff.lint.per-file-ignores] +"tests/**/*.py" = [ + "S101", # asserts allowed in tests + "ARG", # unused function args (pytest fixtures) ] From 5773a5d1d63d719b375202115a9bf9b20c388e78 Mon Sep 17 00:00:00 2001 From: Sebastian Tramp <sebastian.tramp@eccenca.com> Date: Sun, 6 Sep 2026 14:17:20 +0200 Subject: [PATCH 2/3] update to cmem-plugin-template v9.6.1 Replays the v9.5.0 -> v9.6.1 template diff on top of the existing update. This restores the [tool.pytest.ini_options] table, whose absence let pytest's rootdir walk escape the project directory. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UhUJZ7EwjXm7mN2jAdZzvn --- .claude/hooks/template-feedback.py | 284 +++++++++++++----- .claude/rules/copier-template.md | 3 + .claude/settings.json | 2 +- .claude/skills/plugin-implementation/SKILL.md | 50 ++- .copier-answers.yml | 2 +- .github/workflows/check.yml | 13 +- Taskfile.yaml | 80 +++-- pyproject.toml | 7 + 8 files changed, 340 insertions(+), 101 deletions(-) diff --git a/.claude/hooks/template-feedback.py b/.claude/hooks/template-feedback.py index 6c89b37..040637f 100644 --- a/.claude/hooks/template-feedback.py +++ b/.claude/hooks/template-feedback.py @@ -1,18 +1,25 @@ #!/usr/bin/env python3 """Notice friction with the cmem-plugin-template at the end of a session. -This is a Claude Code ``Stop`` hook, wired up in ``.claude/settings.json`` and -run through ``task template:feedback-check``. It belongs to the template - see -``.claude/rules/copier-template.md``. +This is a Claude Code ``Stop`` hook. ``.claude/settings.json`` runs it +directly, and not through a task, because a task runner writes to stdout and +picks its own exit codes - and stdout is this hook's JSON channel. It reads the +hook payload from stdin, so a hand run needs one:: + + echo '{}' | python3 .claude/hooks/template-feedback.py + +It belongs to the template - see ``.claude/rules/copier-template.md``. It says nothing at all unless the session left evidence that a template owned file got in the way, and then it asks the agent to consider whether that is worth reporting upstream. Deciding there is nothing to report is a valid answer; the hook fires at most once per session either way. -Two rules govern everything below. Print **nothing** unless there is something -to say, because stdout is the hook's JSON channel. And always exit 0, whatever -goes wrong - a broken hook must never keep a session from ending. +Three rules govern everything below. Print **nothing** unless there is +something to say, because stdout is the hook's JSON channel. Always exit 0, +whatever goes wrong - a broken hook must never keep a session from ending. And +when a fact cannot be established, drop the evidence rather than guessing: a +false positive blocks a session, a false negative only misses one report. This file is not part of the package and is not covered by ``task check``, so keep it small, dependency free and readable. @@ -23,6 +30,7 @@ import subprocess import sys import tempfile +import tomllib from pathlib import Path OPT_OUT = Path(".claude") / "no-template-feedback" @@ -38,16 +46,25 @@ "Taskfile.yaml", ) -# An added line silencing a check. The rules forbid these outright, so a new -# one always means a rule lost an argument with real code. -SILENCER = re.compile(r"^\+.*#\s*(noqa|type:\s*ignore)") +# The one template owned file that *documents* what a conflict looks like, in +# a fenced example. Every copier update that rewrites it adds those markers to +# the diff, which would block the session during the very workflow this hook +# exists to support. +CONFLICT_PROSE = ".claude/skills/copier-update/SKILL.md" -# A rule code added to a list in pyproject.toml - almost always the ruff -# ignore list growing. -RUFF_RULE = re.compile(r'^\+\s*"[A-Z]{1,4}[0-9]{1,4}"') +# Suffixes the suppression comments below can appear in at all. Without this, +# a CHANGELOG entry or a rules file merely naming `# noqa` counts as one. +PYTHON_SUFFIXES = (".py", ".pyi") -# What copier leaves behind when an update could not be merged. -CONFLICT = re.compile(r"^\+?<{7} ") +# A comment silencing a check. The rules forbid these outright, so a new one +# means a rule lost an argument with real code. Counted per file against the +# removed side, so editing a line that already carried one is not a finding. +SILENCER = re.compile(r"#\s*(noqa|type:\s*ignore)") + +# What copier leaves behind when an update could not be merged. Only an added +# line counts; a marker already committed is this project's problem, not the +# template's. +CONFLICT = re.compile(r"^\+<{7} ") # A changed `_commit` in the copier answers file. Its presence means the # working tree *is* a copier update, so the template owned files it rewrote @@ -58,114 +75,243 @@ # it does even when the output is a pipe. ANSI = re.compile(r"\x1b\[[0-9;]*m") +# Enough of a file to find a suppression in, without reading a data fixture +# somebody left untracked. +READ_LIMIT = 512 * 1024 + +# The shortest useful `git status --porcelain` record: "XY " and one character +# of path. +MIN_STATUS_FIELD = 4 + TIMEOUT = 10 -def git(*args: str) -> str: - """Run a git command and return its plain output, or "" on any failure. +def git(*args: str) -> str | None: + """Run a git command and return its plain output, or None if it failed. + + "Failed" and "said nothing" must stay apart. `git diff HEAD` exits non-zero + on an unborn HEAD, and a caller reading that as an empty diff would go half + blind - seeing every untracked file through `git status` while believing + nothing had changed. The same applies when git is missing from PATH or + another process holds `index.lock`. - Colour is both switched off and stripped afterwards. ``color.diff`` is a - common setting, it outranks ``color.ui``, and it paints diff output even - into a pipe - which would leave every pattern above matching nothing at - all, silently. + The configuration passed here pins the output format the parsers assume: + colour off (`color.diff` outranks `color.ui` and paints even into a pipe), + the `a/` and `b/` diff prefixes on, and paths unquoted. """ + command = [ + "git", + "--no-pager", + "-c", + "color.ui=false", + "-c", + "core.quotePath=false", + "-c", + "diff.noprefix=false", + "-c", + "diff.mnemonicPrefix=false", + *args, + ] try: result = subprocess.run( - ["git", "--no-pager", "-c", "color.ui=false", *args], + command, capture_output=True, text=True, timeout=TIMEOUT, check=False, ) except (OSError, subprocess.SubprocessError): - return "" - return ANSI.sub("", result.stdout) if result.returncode == 0 else "" + return None + if result.returncode != 0: + return None + return ANSI.sub("", result.stdout) -def attributed(diff: str) -> list[tuple[str, str]]: - """Pair every diff line with the file it belongs to. +def entries(status: str) -> list[tuple[str, str]]: + """Return the (status, path) pairs of a `git status --porcelain -uall -z`. - A flat scan of a diff cannot tell a project's own code from a template - owned file that merely *writes about* the thing being looked for - the - rules file discusses `# noqa`, and matching that prose reports the template - to itself. + `-z` is what makes this safe to parse: paths arrive verbatim, so neither + `core.quotePath` nor a space in a name can defeat a prefix or suffix test. + A rename is emitted as the new path followed by the original one, and only + the new path is of any interest here. """ + fields = [field for field in status.split("\0") if field] pairs = [] - path = "" + index = 0 + while index < len(fields): + field = fields[index] + index += 1 + if len(field) < MIN_STATUS_FIELD: + continue + code, path = field[:2], field[3:] + pairs.append((code, path)) + if code[0] in "RC" or code[1] in "RC": + index += 1 # the original path of a rename or copy + return pairs + + +def attributed(diff: str) -> list[tuple[str | None, str]]: + """Pair every content line of a diff with the file it belongs to. + + A flat scan cannot tell a project's own code from a template owned file + that merely *writes about* the thing being looked for - the rules file + discusses `# noqa`, and matching that prose reports the template to itself. + + A line whose file could not be established is paired with None, and every + caller drops those. That happens for a deletion, whose `+++` side is + `/dev/null`, and it is the safe direction: unattributed evidence is no + evidence. + """ + pairs: list[tuple[str | None, str]] = [] + path: str | None = None + in_header = False for line in diff.splitlines(): - if line.startswith("+++ b/"): - # Git pads the path with a tab, and quotes it when it contains - # spaces - which template owned paths do not, in a rendered project. - path = line[6:].split("\t")[0].strip().strip('"') - else: + if line.startswith("diff --git "): + path, in_header = None, True + elif not in_header: pairs.append((path, line)) + elif line.startswith("@@"): + in_header = False + elif line.startswith("+++ b/"): + # Git pads the path with a tab when the name needs it. + path = line[len("+++ b/") :].split("\t")[0] return pairs -def marker_for(session_id: str) -> Path: - """Return the once-per-session marker file for a session id.""" - safe = re.sub(r"[^A-Za-z0-9_-]", "", session_id)[:64] or "unknown" - return Path(tempfile.gettempdir()) / f"cmem-template-feedback-{safe}" +def owned(path: str | None) -> bool: + """Say whether a path is one the template writes.""" + return path is not None and path.startswith(TEMPLATE_OWNED) + + +def added_silencers(pairs: list[tuple[str | None, str]], untracked: list[str]) -> bool: + """Say whether this project gained a suppression comment. + + Three things have to line up. The file must be one this project authored - + a `# noqa` inside a template owned file was written by the template. It + must be Python, or prose naming the comment counts as using it. And the + file must end up with more of them than it started with, so editing a line + that has always carried one is not a finding. + + Untracked files are read from disk, because `git diff HEAD` does not list + them and writing a new module is the most common way a suppression arrives. + """ + counts: dict[str, int] = {} + for path, line in pairs: + if owned(path) or path is None or not path.endswith(PYTHON_SUFFIXES): + continue + if not SILENCER.search(line[1:]): + continue + if line.startswith("+"): + counts[path] = counts.get(path, 0) + 1 + elif line.startswith("-"): + counts[path] = counts.get(path, 0) - 1 + if any(count > 0 for count in counts.values()): + return True + for path in untracked: + if owned(path) or not path.endswith(PYTHON_SUFFIXES): + continue + try: + text = Path(path).read_text(encoding="utf-8", errors="replace")[:READ_LIMIT] + except OSError: + continue + if SILENCER.search(text): + return True + return False def collect_evidence() -> list[str]: """Return human readable evidence of template friction, newest first.""" - evidence = [] - - status = git("status", "--porcelain") + status = git("status", "--porcelain", "-uall", "-z") diff = git("diff", "HEAD") - diff_lines = diff.splitlines() + if status is None or diff is None: + return [] + + pairs = attributed(diff) + listed = entries(status) + evidence = [] # A `copier update` rewrites every template owned path by definition. Do not # report the act of taking a new template version as friction with it - a # suppression written by hand while resolving an update conflict is still a # real finding, and is caught below because it lands in this project's own # source rather than in a template owned file. - updating = any(COPIER_UPDATE.match(line) for line in diff_lines) - - touched = sorted( - { - path - for line in status.splitlines() - for path in [line[3:].split(" -> ")[-1].strip()] - if path.startswith(TEMPLATE_OWNED) - } - ) + updating = any(COPIER_UPDATE.match(line) for _, line in pairs) + + touched = sorted({path for _, path in listed if path.startswith(TEMPLATE_OWNED)}) if touched and not updating: evidence.append(f"template owned files were changed here: {', '.join(touched)}") - rejects = [ - line[3:].strip() for line in status.splitlines() if line[3:].strip().endswith(".rej") - ] + rejects = sorted({path for _, path in listed if path.endswith(".rej")}) if rejects: evidence.append(f"a copier update left rejected hunks behind: {', '.join(rejects)}") - # Only in code this project actually authored. A `# noqa` inside a template - # owned file was written by the template, so it can never be evidence of - # this project working around anything. - if any( - SILENCER.match(line) - for path, line in attributed(diff) - if not path.startswith(TEMPLATE_OWNED) - ): + if added_silencers(pairs, [path for code, path in listed if code == "??"]): evidence.append("a '# noqa' or '# type: ignore' was added") # Conflict markers are the opposite case: one inside a template owned file # is precisely the "the update could not be merged" report worth having. - if any(CONFLICT.match(line) for line in diff_lines): + if any( + CONFLICT.match(line) + for path, line in pairs + if path is not None and path != CONFLICT_PROSE + ): evidence.append("conflict markers are still in the working tree") # `pyproject.toml` is not template owned, but its lint configuration is # rendered - so during an update a new ignore entry arrived with the # template rather than being chosen here. - if not updating and any( - RUFF_RULE.match(line) for line in git("diff", "HEAD", "--", "pyproject.toml").splitlines() - ): - evidence.append("a lint rule was added to the ignore list in pyproject.toml") + try: + current = Path("pyproject.toml").read_text(encoding="utf-8") + except OSError: + current = None + silenced = set() if updating else added_ignores(git("show", "HEAD:pyproject.toml"), current) + if silenced: + evidence.append( + "these lint rules joined the ruff ignore list in pyproject.toml: " + + ", ".join(sorted(silenced)) + ) return evidence +def added_ignores(before: str | None, after: str | None) -> set[str]: + """Return the rule codes `[tool.ruff.lint] ignore` gained, comparing two files. + + Reading the table is the whole point. Matching a quoted rule code against + the diff cannot tell which table it is in, so it fired on `extend-select`, + which *tightens* linting, on a `[tool.deptry]` entry, and on the + `per-file-ignores` relaxation for tests that the shipped rules prescribe - + reporting the project for doing what it was told. + + `per-file-ignores` is deliberately not looked at for that reason. Anything + unreadable on either side yields no codes, because a suppression that + cannot be established is not evidence. + """ + + def ignores(source: str | None) -> set[str] | None: + if source is None: + return None + try: + lint = tomllib.loads(source)["tool"]["ruff"]["lint"] + except (tomllib.TOMLDecodeError, ValueError, KeyError, TypeError): + return None + listed = lint.get("ignore") if isinstance(lint, dict) else None + if not isinstance(listed, list): + return set() + return {code for code in listed if isinstance(code, str)} + + old, new = ignores(before), ignores(after) + if old is None or new is None: + return set() + return new - old + + +def marker_for(session_id: str) -> Path: + """Return the once-per-session marker file for a session id.""" + safe = re.sub(r"[^A-Za-z0-9_-]", "", session_id)[:64] or "unknown" + return Path(tempfile.gettempdir()) / f"cmem-template-feedback-{safe}" + + def main() -> None: """Read the hook payload, and block once if there is something to report.""" try: diff --git a/.claude/rules/copier-template.md b/.claude/rules/copier-template.md index 1ae835f..096a85f 100644 --- a/.claude/rules/copier-template.md +++ b/.claude/rules/copier-template.md @@ -55,3 +55,6 @@ in other projects as well. entry under `## [Unreleased]` in the matching `### Added`, `### Changed`, `### Fixed` or `### Removed` section. The trigger is whether a user would notice, not whether behaviour changed. + +Do not cite an issue or Jira ticket. The people reading this file cannot open +them, so the entry has to stand on its own. diff --git a/.claude/settings.json b/.claude/settings.json index 81b4dd4..b2845b5 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -30,7 +30,7 @@ "hooks": [ { "type": "command", - "command": "task template:feedback-check", + "command": "python3 .claude/hooks/template-feedback.py", "timeout": 30 } ] diff --git a/.claude/skills/plugin-implementation/SKILL.md b/.claude/skills/plugin-implementation/SKILL.md index 231baf0..9eb1206 100644 --- a/.claude/skills/plugin-implementation/SKILL.md +++ b/.claude/skills/plugin-implementation/SKILL.md @@ -106,10 +106,56 @@ input_ports=FixedNumberOfInputs([FixedSchemaPort(schema=MY_SCHEMA)]), output_port=FixedSchemaPort(schema=MY_SCHEMA), ``` -Use `UnknownSchemaPort` when the schema is only known at runtime, and -`FlexibleNumberOfInputs` when the task genuinely accepts any number of inputs. A task that consumes nothing declares `FixedNumberOfInputs([])`. +Two independent things are being declared here, and they are easy to confuse. +`FixedNumberOfInputs` and `FlexibleNumberOfInputs` say how many inputs the task +takes. `FixedSchemaPort`, `FlexibleSchemaPort` and `UnknownSchemaPort` say what +a single port's schema is. `FlexibleNumberOfInputs` makes every one of its +inputs a flexible schema port, which is why the two get read as one choice. + +Prefer a **fixed** schema on every port, and reach for a flexible one only when +the task really cannot know its schema. A fixed schema lets DataIntegration +check a connection while the workflow is being drawn, which turns a runtime +abort into an error the author sees in the editor. + +A flexible **input** schema costs more than it looks - whether it is written +`FixedNumberOfInputs([FlexibleSchemaPort()])` or `FlexibleNumberOfInputs()`. +An operator declaring one has been seen to reject a file dataset outright, +aborting before it receives a single entity, with an entity count of 0 and + +```text +array assignment index out of range: 0 +``` + +A flexible port requests no paths, and reading a file dataset with an empty +requested schema appears to be the trigger - the same file read by a transform, +which requests named paths, is fine. The message names an array index, so it +reads like a bug in the plugin rather than a schema negotiation that never +happened. If a "process whatever arrives" task has to exist, say in its +documentation that its input comes from another task rather than from a dataset. + +`UnknownSchemaPort` is the safer of the two escapes: it says the schema is not +known in advance, without asking DataIntegration to adapt this port to whatever +is connected. + +A port can also depend on a parameter's current value instead of being fixed +at write time. Assign `input_ports`/`output_port` in `__init__` from a +condition on `self`, and the port the editor shows follows the parameter: + +```python +self.input_ports = ( + FixedNumberOfInputs([]) + if self.source_file.strip() + else FixedNumberOfInputs([FixedSchemaPort(schema=MY_SCHEMA)]) +) +``` + +This is the standard way to offer two mutually exclusive ways of supplying the +same thing - a parameter here versus a connected input - rather than accepting +both and picking one at runtime. A boolean or an enum parameter drives the same +pattern with an `if`/`match` in place of the empty-string check. + ## Honouring cancellation A long-running task must stop when the user cancels the workflow. Check the diff --git a/.copier-answers.yml b/.copier-answers.yml index 1ee25b6..be75e2c 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier -_commit: v9.5.0 +_commit: v9.6.1 _src_path: gh:eccenca/cmem-plugin-template author_mail: cmempy-developer@eccenca.com author_name: eccenca GmbH diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index b049176..53fc94b 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -12,6 +12,15 @@ on: # Allows you to run this workflow manually from the Actions tab workflow_dispatch: +# Least privilege, and enough for the two reporting steps at the end of the job: +# the junit report creates check runs, the coverage report writes a pull request +# comment. A pull request from a fork gets a read-only token whatever is written +# here, which is why both of those steps are allowed to fail. +permissions: + contents: read + checks: write + pull-requests: write + jobs: check: @@ -76,12 +85,14 @@ jobs: - name: Publish Test Report in Action uses: mikepenz/action-junit-report@v6 if: always() # always run even if the previous step fails + continue-on-error: true # a fork pull request cannot be given `checks: write` with: report_paths: dist/junit-*.xml - name: Publish Test and Coverage Report as PR comment - uses: xportation/junit-coverage-report@main + uses: xportation/junit-coverage-report@v1.0.3 if: github.event_name == 'pull_request' + continue-on-error: true # a fork pull request cannot comment on itself with: junit-path: dist/junit-pytest.xml coverage-path: dist/coverage.xml diff --git a/Taskfile.yaml b/Taskfile.yaml index 715e490..aade49a 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -7,12 +7,11 @@ version: '3' dotenv: ['.copier-answers.env', '.env'] -.preparation: &preparation - deps: - - poetry:install - - check:prepare - vars: + # $package_dir is expanded by the shell, from the .copier-answers.env entry + # that `dotenv` above loads. An exported package_dir of your own outranks that + # entry, which the `package:exists` task below turns into a loud failure + # instead of a check that quietly looks at nothing. PACKAGE: $package_dir DIST_DIR: dist @@ -36,11 +35,33 @@ tasks: - task --list # {{{ preparation tasks - check:prepare: + # These are plain internal tasks rather than a YAML anchor on purpose. A merge + # key only supplies keys the mapping does not already have, so a task with any + # `deps` of its own silently loses everything the anchor meant to add - which + # is what `build` used to do. Naming them in a `deps` list composes instead. + package:exists: internal: true summary: | - prepare check targets by creating appropriate directory + fail loudly when PACKAGE does not name a directory run: once + preconditions: + - sh: '[ -d "$package_dir" ]' + msg: > + The package directory does not exist. Its name comes from the + package_dir entry of .copier-answers.env, and an exported package_dir + of your own silently outranks that entry - unset it. Without this + check, an empty value dropped the package from every command line and + the checks passed having looked at nothing but tests. + + prepare: + internal: true + summary: | + everything a check or format task needs first: the virtualenv, a package + directory that exists, and the directory the reports are written to + run: once + deps: + - poetry:install + - package:exists cmds: - mkdir -p {{.DIST_DIR}}/coverage @@ -50,32 +71,29 @@ tasks: cmds: - poetry install + # Neither format task depends on `poetry:install`, unlike the check tasks. + # `format:fix` is what the Claude Code PostToolUse hook runs after every edit, + # and `poetry install` refuses to run while pyproject.toml has been changed + # without the lock file being regenerated - exactly what a dependency bump or + # a template update looks like. Formatting would then quietly stop for the + # rest of the session. On a clone with no virtualenv yet, run `task check` or + # `task poetry:install` first. format:fix: desc: Format Python files and fix obvious issues - <<: *preparation + deps: + - package:exists cmds: - poetry run ruff format tests {{.PACKAGE}} - poetry run ruff check tests {{.PACKAGE}} --fix-only format:fix-unsafe: desc: Format Python files and fix 'unsafe' issues - <<: *preparation + deps: + - package:exists cmds: - poetry run ruff format tests {{.PACKAGE}} - poetry run ruff check tests {{.PACKAGE}} --fix-only --unsafe-fixes - template:feedback-check: - desc: Look for findings worth reporting to the cmem-plugin-template - summary: | - Reads a Claude Code Stop hook payload on stdin and asks the agent to - consider reporting upstream when this working tree shows that a template - owned file got in the way. Prints nothing when there is nothing to say. - - Create an empty .claude/no-template-feedback file to switch it off. - silent: true - cmds: - - python3 .claude/hooks/template-feedback.py - clean: desc: Removes dist, *.pyc and some caches cmds: @@ -102,7 +120,8 @@ tasks: check:pytest: desc: Run unit and integration tests platforms: [darwin, linux, windows] - <<: *preparation + deps: + - prepare cmds: # --memray is not used on windows - platforms: [windows] @@ -133,7 +152,8 @@ tasks: check:mypy: desc: Complain about typing errors - <<: *preparation + deps: + - prepare cmds: - poetry run mypy -p tests -p {{.PACKAGE}} --junit-xml {{.JUNIT_FILE}} vars: @@ -141,7 +161,8 @@ tasks: check:trivy: desc: Scan for vulnerabilities using Trivy - <<: *preparation + deps: + - prepare cmds: - > poetry run trivy fs @@ -153,13 +174,15 @@ tasks: check:deptry: desc: Complain about unused or missing dependencies - <<: *preparation + deps: + - prepare cmds: - poetry run deptry . check:ruff: desc: Complain about everything else - <<: *preparation + deps: + - prepare cmds: - poetry run ruff check --exit-zero tests {{.PACKAGE}} {{.XML_PARAMS}} - poetry run ruff check --output-format=concise tests {{.PACKAGE}} @@ -172,8 +195,11 @@ tasks: build: desc: Build a tarball and a wheel package - <<: *preparation + # `prepare` is deliberately not among these: `build` does not write into + # dist/coverage, and it would race with `clean`, since Task runs deps in + # parallel. deps: + - poetry:install - clean cmds: - poetry build diff --git a/pyproject.toml b/pyproject.toml index 18f4d42..997422d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,6 +58,13 @@ bump = true warn_return_any = true ignore_missing_imports = true +# Deliberately empty. pytest walks upward until a file *loads* a configuration, +# and only the presence of this table makes pyproject.toml one - without it the +# walk carries on into the parent directories, where any pytest.ini, tox.ini or +# setup.cfg, even an empty one, would set this project's rootdir and could push +# its own addopts into this test suite. Do not tidy it away. +[tool.pytest.ini_options] + [tool.coverage.report] exclude_also = [ "def __repr__", From 8d8aeab0fedd8326e2b032d5e4a3a89da085b94e Mon Sep 17 00:00:00 2001 From: Sebastian Tramp <sebastian.tramp@eccenca.com> Date: Sun, 6 Sep 2026 20:46:47 +0200 Subject: [PATCH 3/3] tests: (re-)create the test project instead of failing when it exists The project fixture called projects.create_item on a fixed id without clearing it first. Any run that died before the teardown left validate_entities_test_project behind on the shared Corporate Memory instance, and every later run then errored at setup with "Item with id validate_entities_test_project already exists". Deleting with skip_if_missing before creating matches what cmem-plugin-kafka already does in tests/utils.make_project. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UhUJZ7EwjXm7mN2jAdZzvn --- tests/test_validate_entities.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_validate_entities.py b/tests/test_validate_entities.py index 0be8d08..633260b 100644 --- a/tests/test_validate_entities.py +++ b/tests/test_validate_entities.py @@ -60,6 +60,9 @@ def project() -> Generator[TestSetup]: """Provide the DI build project incl. assets.""" _ = TestSetup() client = get_client(_.project_name) + # a run which dies before the teardown below leaves the project behind, and + # create_item then fails for every later run - so (re-)create it + client.projects.delete_item(_.project_name, skip_if_missing=True) client.projects.create_item(Project(name=_.project_name)) _make_dataset(client, _.project_name, _.target_dataset, _.target_dataset_file) for dataset_name, dataset_file in (