🔖 Bump version: 0.2.5 → 0.2.6
This commit is contained in:
parent
46f4f74dc8
commit
39c7a94ceb
18 changed files with 1811 additions and 0 deletions
267
.gitignore
vendored
Normal file
267
.gitignore
vendored
Normal file
|
@ -0,0 +1,267 @@
|
|||
# Created by https://www.toptal.com/developers/gitignore/api/pycharm+all
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=pycharm+all
|
||||
|
||||
### Python ###
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
### Python Patch ###
|
||||
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
|
||||
poetry.toml
|
||||
|
||||
# ruff
|
||||
.ruff_cache/
|
||||
|
||||
# LSP config files
|
||||
pyrightconfig.json
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/python
|
||||
|
||||
### PyCharm+all ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# AWS User-specific
|
||||
.idea/**/aws.xml
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# SonarLint plugin
|
||||
.idea/sonarlint/
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### PyCharm+all Patch ###
|
||||
# Ignore everything but code style settings and run configurations
|
||||
# that are supposed to be shared within teams.
|
||||
|
||||
.idea/*
|
||||
|
||||
!.idea/codeStyles
|
||||
!.idea/runConfigurations
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/pycharm+all
|
||||
|
9
LICENSE.md
Normal file
9
LICENSE.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
# The MIT License (MIT)
|
||||
|
||||
Copyright © 2025 Marc Koch marc.koch@posteo.de
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
6
civifang.egg-info/PKG-INFO
Normal file
6
civifang.egg-info/PKG-INFO
Normal file
|
@ -0,0 +1,6 @@
|
|||
Metadata-Version: 2.2
|
||||
Name: civifang
|
||||
Version: 0.1.0
|
||||
Summary: A Python package to connect communicate with CiviCRM instances.
|
||||
Author-email: Marc Koch <marc.koch@propeace.de>
|
||||
Requires-Python: >=3.12
|
6
civifang.egg-info/SOURCES.txt
Normal file
6
civifang.egg-info/SOURCES.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
main.py
|
||||
pyproject.toml
|
||||
civifang.egg-info/PKG-INFO
|
||||
civifang.egg-info/SOURCES.txt
|
||||
civifang.egg-info/dependency_links.txt
|
||||
civifang.egg-info/top_level.txt
|
1
civifang.egg-info/dependency_links.txt
Normal file
1
civifang.egg-info/dependency_links.txt
Normal file
|
@ -0,0 +1 @@
|
|||
|
1
civifang.egg-info/top_level.txt
Normal file
1
civifang.egg-info/top_level.txt
Normal file
|
@ -0,0 +1 @@
|
|||
main
|
51
pyproject.toml
Normal file
51
pyproject.toml
Normal file
|
@ -0,0 +1,51 @@
|
|||
[project]
|
||||
name = "civifang"
|
||||
version = "0.2.6"
|
||||
description = "A Python package to communicate with CiviCRM instances."
|
||||
requires-python = ">=3.12"
|
||||
authors = [
|
||||
{ name = "Marc Koch", email = "marc.koch@propeace.de" },
|
||||
]
|
||||
dependencies = [
|
||||
"httpx>=0.28.1",
|
||||
"validators>=0.34.0",
|
||||
]
|
||||
|
||||
[[tool.uv.index]]
|
||||
name = "forgejo"
|
||||
url = "https://git.extrasolar.space/api/packages/marc/pypi"
|
||||
publish-url = "https://git.extrasolar.space/api/packages/marc/pypi"
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"bandit>=1.8.3",
|
||||
"black>=25.1.0",
|
||||
"bump-my-version>=1.0.2",
|
||||
"uv>=0.6.5",
|
||||
]
|
||||
|
||||
[tool.bumpversion]
|
||||
current_version = "0.2.6"
|
||||
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
||||
serialize = ["{major}.{minor}.{patch}"]
|
||||
search = "{current_version}"
|
||||
replace = "{new_version}"
|
||||
regex = false
|
||||
files = [
|
||||
{filename = "src/civifang/__init__.py"}
|
||||
]
|
||||
ignore_missing_version = false
|
||||
ignore_missing_files = false
|
||||
tag = false
|
||||
sign_tags = true
|
||||
tag_name = "v{new_version}"
|
||||
tag_message = "Bump version: {current_version} → {new_version}"
|
||||
allow_dirty = false
|
||||
commit = true
|
||||
message = "🔖 Bump version: {current_version} → {new_version}"
|
||||
moveable_tags = []
|
||||
commit_args = ""
|
||||
setup_hooks = []
|
||||
pre_commit_hooks = []
|
||||
post_commit_hooks = []
|
||||
|
9
src/civifang.egg-info/PKG-INFO
Normal file
9
src/civifang.egg-info/PKG-INFO
Normal file
|
@ -0,0 +1,9 @@
|
|||
Metadata-Version: 2.2
|
||||
Name: civifang
|
||||
Version: 0.2.6
|
||||
Summary: A Python package to communicate with CiviCRM instances.
|
||||
Author-email: Marc Koch <marc.koch@propeace.de>
|
||||
Requires-Python: >=3.12
|
||||
License-File: LICENSE.md
|
||||
Requires-Dist: httpx>=0.28.1
|
||||
Requires-Dist: validators>=0.34.0
|
13
src/civifang.egg-info/SOURCES.txt
Normal file
13
src/civifang.egg-info/SOURCES.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
LICENSE.md
|
||||
README.md
|
||||
pyproject.toml
|
||||
src/civifang/__init__.py
|
||||
src/civifang/entities.py
|
||||
src/civifang/enums.py
|
||||
src/civifang/exceptions.py
|
||||
src/civifang/models.py
|
||||
src/civifang.egg-info/PKG-INFO
|
||||
src/civifang.egg-info/SOURCES.txt
|
||||
src/civifang.egg-info/dependency_links.txt
|
||||
src/civifang.egg-info/requires.txt
|
||||
src/civifang.egg-info/top_level.txt
|
1
src/civifang.egg-info/dependency_links.txt
Normal file
1
src/civifang.egg-info/dependency_links.txt
Normal file
|
@ -0,0 +1 @@
|
|||
|
2
src/civifang.egg-info/requires.txt
Normal file
2
src/civifang.egg-info/requires.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
httpx>=0.28.1
|
||||
validators>=0.34.0
|
1
src/civifang.egg-info/top_level.txt
Normal file
1
src/civifang.egg-info/top_level.txt
Normal file
|
@ -0,0 +1 @@
|
|||
civifang
|
11
src/civifang/__init__.py
Normal file
11
src/civifang/__init__.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
import logging
|
||||
|
||||
from .models import CiviCrmApi, AsyncCiviCrmApi
|
||||
from .models import Api4
|
||||
|
||||
__version__ = "0.2.6"
|
||||
__author__ = "Marc Koch"
|
||||
|
||||
# Initialize global api instances
|
||||
api = CiviCrmApi()
|
||||
async_api = AsyncCiviCrmApi()
|
8
src/civifang/entities.py
Normal file
8
src/civifang/entities.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from .models import Api4
|
||||
|
||||
|
||||
class Contact:
|
||||
|
||||
@classmethod
|
||||
def get(cls, check_permissions: bool = True):
|
||||
return Api4.get("Contact", check_permissions)
|
37
src/civifang/enums.py
Normal file
37
src/civifang/enums.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
from enum import Enum, auto
|
||||
|
||||
|
||||
class JoinTypes(Enum):
|
||||
"""
|
||||
Enum for the different join types.
|
||||
"""
|
||||
LEFT = "LEFT"
|
||||
INNER = "INNER"
|
||||
EXCLUDE = "EXCLUDE"
|
||||
|
||||
|
||||
class OrderByDirections(Enum):
|
||||
"""
|
||||
Enum for the different order by directions.
|
||||
"""
|
||||
ASC = "ASC"
|
||||
DESC = "DESC"
|
||||
|
||||
|
||||
class AuthFlow(Enum):
|
||||
"""
|
||||
Enum for the different authentication flows.
|
||||
"""
|
||||
XHEADER = auto()
|
||||
COMMON = auto()
|
||||
PARAMETER = auto()
|
||||
LEGACY = auto()
|
||||
LOGIN_SESSION = auto()
|
||||
|
||||
|
||||
class HttpMethods(Enum):
|
||||
"""
|
||||
List of supported HTTP methods
|
||||
"""
|
||||
GET = "get"
|
||||
POST = "post"
|
29
src/civifang/exceptions.py
Normal file
29
src/civifang/exceptions.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
class CiviFangBaseException(Exception):
|
||||
"""Custom base exception"""
|
||||
|
||||
|
||||
class ApiAuthError(CiviFangBaseException):
|
||||
"""Custom exception: authentication failed"""
|
||||
|
||||
|
||||
class ApiError(CiviFangBaseException):
|
||||
"""Custom exception: failed API call"""
|
||||
|
||||
|
||||
class BuildQueryException(CiviFangBaseException):
|
||||
"""Custom exception: failed to build query"""
|
||||
|
||||
|
||||
class ApiException(CiviFangBaseException):
|
||||
"""Custom exception: failed API call"""
|
||||
|
||||
|
||||
class SslException(CiviFangBaseException):
|
||||
"""Custom exception: not using ssl"""
|
||||
|
||||
def __init__(self, message: str = None):
|
||||
self.message = message if message else (
|
||||
"The provided url does not start with 'https'. "
|
||||
"You can force the use of an unencrypted connection, but you "
|
||||
"should be very careful and only do this in test environments "
|
||||
"without any data of real people!")
|
825
src/civifang/models.py
Normal file
825
src/civifang/models.py
Normal file
|
@ -0,0 +1,825 @@
|
|||
import logging
|
||||
from abc import ABC, abstractmethod
|
||||
from base64 import b64encode
|
||||
from typing import Self
|
||||
|
||||
import validators
|
||||
from httpx import Client, Cookies, BasicAuth, Limits, AsyncClient, Request
|
||||
|
||||
from .enums import AuthFlow, HttpMethods
|
||||
from .enums import JoinTypes, OrderByDirections
|
||||
from .exceptions import (
|
||||
ApiAuthError,
|
||||
ApiError,
|
||||
ApiException,
|
||||
SslException,
|
||||
)
|
||||
from .exceptions import BuildQueryException
|
||||
|
||||
_ACTION_TO_METHOD_MAPPING = {
|
||||
"get": HttpMethods.GET,
|
||||
"create": HttpMethods.POST,
|
||||
"update": HttpMethods.POST,
|
||||
}
|
||||
|
||||
logger = logging.getLogger(__package__)
|
||||
|
||||
|
||||
def get_method(action: str) -> HttpMethods:
|
||||
"""
|
||||
Return the method HttpMethod Enum for an action
|
||||
:param action:
|
||||
"""
|
||||
return _ACTION_TO_METHOD_MAPPING.get(action)
|
||||
|
||||
|
||||
# noinspection PyAttributeOutsideInit,PyUnresolvedReferences
|
||||
class Api4:
|
||||
"""
|
||||
Generic class for API v4 entities.
|
||||
"""
|
||||
|
||||
JOIN_TYPES = JoinTypes
|
||||
ORDER_BY_DIRECTIONS = OrderByDirections
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Do not use the constructor directly. Use an action as a factory method.
|
||||
"""
|
||||
raise RuntimeError("Use a factory method like 'get()' to create an instance.")
|
||||
|
||||
@classmethod
|
||||
def _create_instance(cls, entity: str, action: str):
|
||||
instance = object.__new__(cls)
|
||||
instance._entity = entity
|
||||
instance._action = action
|
||||
instance._check_permissions = True
|
||||
instance._limit = 0
|
||||
instance._offset = 0
|
||||
instance._language = None
|
||||
instance._translation_mode = None
|
||||
instance._selects = []
|
||||
instance._joins = []
|
||||
instance._wheres = []
|
||||
instance._group_by = []
|
||||
instance._order_by = {}
|
||||
instance._having = []
|
||||
instance._chains = {}
|
||||
return instance
|
||||
|
||||
@classmethod
|
||||
def get(cls, entity: str, check_permissions: bool = True):
|
||||
"""
|
||||
Initialize a new get query
|
||||
:param entity: A CiviCRM entity
|
||||
:param check_permissions:
|
||||
:return:
|
||||
"""
|
||||
instance = cls._create_instance(entity, "get")
|
||||
instance._check_permissions = check_permissions
|
||||
return instance
|
||||
|
||||
def set_limit(self, limit: int):
|
||||
"""
|
||||
Set the limit for the query
|
||||
:param limit:
|
||||
:return:
|
||||
"""
|
||||
self._limit = limit
|
||||
return self
|
||||
|
||||
def set_offset(self, offset: int):
|
||||
"""
|
||||
Set the offset for the query
|
||||
:param offset:
|
||||
:return:
|
||||
"""
|
||||
self._offset = offset
|
||||
return self
|
||||
|
||||
def set_language(self, language: str):
|
||||
"""
|
||||
Set the language for the query
|
||||
:param language: e.g. 'de_DE'
|
||||
:return:
|
||||
"""
|
||||
self._language = language
|
||||
return self
|
||||
|
||||
def set_translation_mode(self, mode: str):
|
||||
"""
|
||||
Set the translation mode for the query
|
||||
:param mode: 'fuzzy' or 'strict'
|
||||
:return:
|
||||
"""
|
||||
self._translation_mode = mode
|
||||
return
|
||||
|
||||
def select_row_count(self):
|
||||
"""
|
||||
Add the row count to the select statement
|
||||
:return:
|
||||
"""
|
||||
self._selects.append("row_count")
|
||||
return
|
||||
|
||||
def add_select(self, key: str):
|
||||
"""
|
||||
Add a key to the select statement
|
||||
:param key:
|
||||
:return:
|
||||
"""
|
||||
self._selects.append(key)
|
||||
return self
|
||||
|
||||
def set_select(self, select: list):
|
||||
"""
|
||||
Set the full select statement
|
||||
:param select:
|
||||
:return:
|
||||
"""
|
||||
self._selects = select
|
||||
return self
|
||||
|
||||
def add_join(self, entity: str, join_type: JoinTypes, on_clause: list):
|
||||
"""
|
||||
Add a join clause
|
||||
:param on_clause:
|
||||
:param join_type:
|
||||
:param entity:
|
||||
:return:
|
||||
"""
|
||||
self._joins.append([entity, join_type.value, on_clause])
|
||||
return self
|
||||
|
||||
def set_join(self, joins: list):
|
||||
"""
|
||||
Set the full join statement
|
||||
:param joins:
|
||||
:return:
|
||||
"""
|
||||
self._joins = joins
|
||||
return self
|
||||
|
||||
def add_where(self, where: list):
|
||||
"""
|
||||
Add a where clause
|
||||
:param where:
|
||||
:return:
|
||||
"""
|
||||
self._wheres.append(where)
|
||||
return self
|
||||
|
||||
def set_where(self, where: list):
|
||||
"""
|
||||
Set the full where clause
|
||||
:param where:
|
||||
:return:
|
||||
"""
|
||||
self._wheres = where
|
||||
return self
|
||||
|
||||
def add_group_by(self, key: str):
|
||||
"""
|
||||
Add a group by key
|
||||
:param key:
|
||||
:return:
|
||||
"""
|
||||
self._group_by.append(key)
|
||||
return self
|
||||
|
||||
def set_group_by(self, group_by: list):
|
||||
"""
|
||||
Set the full group by statement
|
||||
:param group_by:
|
||||
:return:
|
||||
"""
|
||||
self._group_by = group_by
|
||||
return self
|
||||
|
||||
def add_having(self, having: list):
|
||||
"""
|
||||
Add a having clause
|
||||
:param having:
|
||||
:return:
|
||||
"""
|
||||
self._having.append(having)
|
||||
return self
|
||||
|
||||
def set_having(self, having: list):
|
||||
"""
|
||||
Set the full having clause
|
||||
:param having:
|
||||
:return:
|
||||
"""
|
||||
self._having = having
|
||||
return self
|
||||
|
||||
def add_order_by(self, key: str, direction: str):
|
||||
"""
|
||||
Add an order-by key
|
||||
:param key:
|
||||
:param direction: 'ASC' or 'DESC'
|
||||
:return:
|
||||
"""
|
||||
self._order_by.update({key: direction})
|
||||
return self
|
||||
|
||||
def set_order_by(self, order_by: dict):
|
||||
"""
|
||||
Set the full order by statement
|
||||
:param order_by:
|
||||
:return:
|
||||
"""
|
||||
self._order_by = order_by
|
||||
return self
|
||||
|
||||
def add_chain(self, name: str, entity: Self):
|
||||
"""
|
||||
Add a chained request
|
||||
:param name: Name of the chained request
|
||||
:param entity: Api4 instance (do not execute it)
|
||||
:return:
|
||||
"""
|
||||
self._chains.update({name: entity})
|
||||
return self
|
||||
|
||||
def set_chain(self, chain: dict):
|
||||
"""
|
||||
Set the full chain statement
|
||||
:param chain:
|
||||
:return:
|
||||
"""
|
||||
self._chains = chain
|
||||
return self
|
||||
|
||||
def execute(self) -> dict:
|
||||
"""
|
||||
Execute the query
|
||||
:return: Response
|
||||
"""
|
||||
from . import api
|
||||
|
||||
method = get_method(self._action)
|
||||
return api.execute_api4(method, self)
|
||||
|
||||
def aexecute(self) -> dict:
|
||||
"""
|
||||
Execute the query asynchronously
|
||||
:return: Response
|
||||
"""
|
||||
from . import async_api
|
||||
|
||||
method = get_method(self._action)
|
||||
return async_api.execute_api4(method, self)
|
||||
|
||||
@property
|
||||
def entity(self) -> str:
|
||||
"""
|
||||
Get the entity
|
||||
:return: str
|
||||
"""
|
||||
return self._entity
|
||||
|
||||
@property
|
||||
def action(self) -> str:
|
||||
"""
|
||||
Get the action
|
||||
:return: str
|
||||
"""
|
||||
return self._action
|
||||
|
||||
def build_query(self) -> dict:
|
||||
"""
|
||||
Build the query
|
||||
:return: dict
|
||||
"""
|
||||
|
||||
if not self._entity:
|
||||
raise BuildQueryException("No entity provided")
|
||||
|
||||
if not self._action:
|
||||
raise BuildQueryException("No action provided")
|
||||
|
||||
# Build chain
|
||||
chain = {}
|
||||
for c in self._chains:
|
||||
chain_name = c.get("name")
|
||||
entity = c.get("entity")
|
||||
chain.update(
|
||||
entity.build_query(
|
||||
{chain_name: [entity.entity, entity.action, entity.build_query()]}
|
||||
)
|
||||
)
|
||||
|
||||
# Build query
|
||||
query = {"checkPermissions": self._check_permissions}
|
||||
if language := self._language:
|
||||
query["language"] = language
|
||||
if translation_mode := self._translation_mode:
|
||||
query["translationMode"] = translation_mode
|
||||
if limit := self._limit:
|
||||
query["limit"] = limit
|
||||
if offset := self._offset:
|
||||
query["offset"] = offset
|
||||
if select := self._selects or ["*"]:
|
||||
query["select"] = select
|
||||
if join := self._joins:
|
||||
query["join"] = join
|
||||
if where := self._wheres:
|
||||
query["where"] = where
|
||||
if group_by := self._group_by:
|
||||
query["groupBy"] = group_by
|
||||
if order_by := self._order_by:
|
||||
query["orderBy"] = order_by
|
||||
if having := self._having:
|
||||
query["having"] = having
|
||||
if chain:
|
||||
query["chain"] = chain
|
||||
|
||||
return query
|
||||
|
||||
|
||||
class BaseCiviCrmApi(ABC):
|
||||
"""
|
||||
This is the base class for the CiviCRM API.
|
||||
"""
|
||||
|
||||
AUTH_FLOWS = AuthFlow
|
||||
HTTP_METHODS = HttpMethods
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._kwargs = kwargs
|
||||
self.client: Client | AsyncClient | None = None
|
||||
self.cookies: Cookies | None = None
|
||||
self.auth_flow: AuthFlow | None = None
|
||||
self.headers: dict = {}
|
||||
self.auth_params: dict = {}
|
||||
self.is_logged_in: bool = False
|
||||
self.user_id: int | None = None
|
||||
self.contact_id: int | None = None
|
||||
self.limits: Limits | None = None
|
||||
self._client_class: type(Client) | type(AsyncClient) | None = None
|
||||
self._api_key: str | None = None
|
||||
self._site_key: str | None = None
|
||||
self._username: str | None = None
|
||||
self._password: str | None = None
|
||||
self._base_url: str | None = None
|
||||
self._url_api4: str | None = None
|
||||
self._url_api3: str | None = None
|
||||
self._url_auth_login: str | None = None
|
||||
self._basic_auth: tuple | None = None
|
||||
self._timeout: int = 300
|
||||
self._ignore_ssl: bool = False
|
||||
self._call_count: int = 0
|
||||
self._is_ready: bool = False
|
||||
|
||||
def setup(
|
||||
self,
|
||||
url: str,
|
||||
auth_flow: AuthFlow,
|
||||
api_key: str,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
Basic setup for the client.
|
||||
:param url:
|
||||
:param auth_flow:
|
||||
:param api_key:
|
||||
:param kwargs:
|
||||
:return:
|
||||
"""
|
||||
|
||||
# Check URL
|
||||
if not validators.url(url):
|
||||
raise IOError(f"'{url}' is not a valid URL")
|
||||
|
||||
# Check SSL
|
||||
self._ignore_ssl = kwargs.get("ignore_ssl", False)
|
||||
if not url.startswith("https"):
|
||||
logger.warning("The provided URL does not start with 'https'!")
|
||||
if not self._ignore_ssl:
|
||||
raise SslException()
|
||||
|
||||
# Set up url and api endpoints
|
||||
self._base_url = url
|
||||
self._url_api3 = "/civicrm/ajax/rest"
|
||||
self._url_api4 = "/civicrm/ajax/api4"
|
||||
self._url_auth_login = "/civicrm/authx/login"
|
||||
|
||||
# Gather authentication details
|
||||
self.auth_flow = auth_flow
|
||||
self._api_key = api_key
|
||||
self._site_key = kwargs.get("site_key", None)
|
||||
self._username = kwargs.get("username", None)
|
||||
self._password = kwargs.get("password", None)
|
||||
|
||||
# Set up authentication
|
||||
self._set_up_authentication()
|
||||
|
||||
# Set up basic auth
|
||||
self._basic_auth = (
|
||||
BasicAuth(*kwargs.get("basic_auth"))
|
||||
if kwargs.get("basic_auth", False)
|
||||
else None
|
||||
)
|
||||
|
||||
# Configure timeout
|
||||
self._timeout = kwargs.get("timeout", 300)
|
||||
|
||||
# Configure number of concurrent connections
|
||||
self.limits = Limits(max_connections=kwargs.get("max_connections", 10))
|
||||
|
||||
# Configure cookies
|
||||
if not self.cookies:
|
||||
self.cookies = Cookies()
|
||||
|
||||
# Create client
|
||||
self.client = self._client_class(
|
||||
base_url=self._base_url,
|
||||
headers=self.headers,
|
||||
verify=not self._ignore_ssl,
|
||||
cookies=self.cookies,
|
||||
auth=self._basic_auth,
|
||||
timeout=self._timeout,
|
||||
trust_env=kwargs.get("trust_env", False),
|
||||
follow_redirects=kwargs.get("follow_redirects", False),
|
||||
limits=self.limits,
|
||||
)
|
||||
|
||||
self._is_ready = True
|
||||
|
||||
def _set_up_authentication(self):
|
||||
"""
|
||||
Set up authentication based on the selected auth flow.
|
||||
:return:
|
||||
"""
|
||||
match self.auth_flow:
|
||||
case AuthFlow.XHEADER:
|
||||
self.headers["X-Civi-Auth"] = f"Bearer {self._api_key}"
|
||||
if self._site_key:
|
||||
self.headers["X-Civi-Key"] = self._site_key
|
||||
case AuthFlow.COMMON:
|
||||
self.headers["Authorization"] = f"Bearer {self._api_key}"
|
||||
if self._site_key:
|
||||
self.headers["X-Civi-Key"] = self._site_key
|
||||
case AuthFlow.PARAMETER:
|
||||
self.auth_params["_authx"] = f"Bearer+{self._api_key}"
|
||||
if self._site_key:
|
||||
self.auth_params["_authxSiteKey"] = self._site_key
|
||||
case AuthFlow.LEGACY:
|
||||
if not self._site_key:
|
||||
message = (
|
||||
"Please provide a site key if you are using the "
|
||||
"'legacy' authentication flow."
|
||||
)
|
||||
raise ApiAuthError(message)
|
||||
self.auth_params["api_key"] = self._api_key
|
||||
self.auth_params["key"] = self._site_key
|
||||
case AuthFlow.LOGIN_SESSION:
|
||||
if not self._username or not self._password:
|
||||
message = (
|
||||
"Please provide a username and a password if "
|
||||
"you are using the 'login_session' "
|
||||
"authentication flow"
|
||||
)
|
||||
raise ApiAuthError(message)
|
||||
user_password = f"{self._username}:{self._password}"
|
||||
cred_bytes = user_password.encode("utf-8")
|
||||
cred_b64 = b64encode(cred_bytes).decode("utf-8")
|
||||
self.auth_params["_authx"] = f"Basic+{cred_b64}"
|
||||
if self._site_key:
|
||||
self.auth_params["_authxSiteKey"] = self._site_key
|
||||
case _:
|
||||
message = f"Authentication flow '{self.auth_flow}' is not " "supported."
|
||||
raise ApiAuthError(message)
|
||||
|
||||
def _prepare_request(
|
||||
self,
|
||||
method: HttpMethods,
|
||||
url: str,
|
||||
params: dict = None,
|
||||
data: dict = None,
|
||||
json_data: dict = None,
|
||||
):
|
||||
"""
|
||||
Prepare the request.
|
||||
:return:
|
||||
"""
|
||||
# Check if the client is ready
|
||||
if not self.is_ready():
|
||||
raise ApiException(
|
||||
"Client is not yet set up." "Please call 'connect()' first."
|
||||
)
|
||||
|
||||
# Log the call for debugging purposes
|
||||
logger.debug(
|
||||
f"Preparing request to: {url}",
|
||||
extra={
|
||||
"method": method.value,
|
||||
"params": params,
|
||||
"data": data,
|
||||
"auth_flow": self.auth_flow,
|
||||
"timeout": self.client.timeout,
|
||||
"ignore_ssl": self._ignore_ssl,
|
||||
"call_count": self._call_count,
|
||||
},
|
||||
)
|
||||
|
||||
# Build the request
|
||||
return self.client.build_request(
|
||||
method.value,
|
||||
url,
|
||||
headers=self.headers,
|
||||
params=self.auth_params | (params or {}),
|
||||
data=data,
|
||||
json=json_data,
|
||||
)
|
||||
|
||||
@abstractmethod
|
||||
def _send_request(self, request):
|
||||
"""
|
||||
Send the request.
|
||||
Must be implemented by the child class synchronously or asynchronously.
|
||||
:return:
|
||||
"""
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def _handle_response(response):
|
||||
"""
|
||||
Allgemeine Logik zur Behandlung von Antworten
|
||||
"""
|
||||
response.raise_for_status()
|
||||
content = response.json()
|
||||
if content.get("is_error"):
|
||||
raise ApiException(content.get("error_message"))
|
||||
return content
|
||||
|
||||
def api3(
|
||||
self,
|
||||
entity: str,
|
||||
action: str,
|
||||
query: dict,
|
||||
method: HttpMethods = None,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
Execute an APIv3 call.
|
||||
:param entity:
|
||||
:param action:
|
||||
:param query:
|
||||
:param method: The HTTP method to use
|
||||
:return:
|
||||
:return:
|
||||
"""
|
||||
url = self._url_api3
|
||||
data = None
|
||||
params = {
|
||||
"entity": entity,
|
||||
"action": action,
|
||||
"sequential": 1,
|
||||
"json": 1,
|
||||
}
|
||||
if method is None:
|
||||
method = get_method(action)
|
||||
|
||||
# In case of a GET request, data has to be submitted as query params
|
||||
if method == self.HTTP_METHODS.GET and query:
|
||||
params.update(query)
|
||||
# In case of a POST request, data can be submitted form-encoded
|
||||
else:
|
||||
data = query
|
||||
|
||||
request = self._prepare_request(method, url, params=params, data=data)
|
||||
result = self._send_request(request)
|
||||
return self._handle_response(result)
|
||||
|
||||
def execute_api4(self, method: HttpMethods, entity: Api4):
|
||||
"""
|
||||
Execute an APIv4 call by handing an APIv4 instance.
|
||||
Better call do not call this method directly but via Entity.execute()
|
||||
:param method:
|
||||
:param entity: An instance of Api4
|
||||
:return:
|
||||
"""
|
||||
url = self._url_api4 + f"/{entity.entity}/{entity.action}"
|
||||
data = entity.build_query() # Todo: Does not work yet
|
||||
|
||||
request = self._prepare_request(method, url, json_data=data)
|
||||
result = self._send_request(request)
|
||||
return self._handle_response(result)
|
||||
|
||||
def is_ready(self):
|
||||
"""
|
||||
Check if the client is ready.
|
||||
:return:
|
||||
"""
|
||||
return self._is_ready
|
||||
|
||||
@abstractmethod
|
||||
def disconnect(self):
|
||||
"""
|
||||
Disconnect from the API.
|
||||
:return:
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def login(self):
|
||||
"""
|
||||
Log in if the auth flow is login session.
|
||||
:return:
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class CiviCrmApi(BaseCiviCrmApi):
|
||||
"""
|
||||
This class provides an interface to CiviCRM APIv4.
|
||||
In most cases, you should not instantiate this class directly but use the
|
||||
global api instance from the package.
|
||||
"""
|
||||
|
||||
def setup(
|
||||
self,
|
||||
url: str,
|
||||
auth_flow: AuthFlow,
|
||||
api_key: str,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
|
||||
self._client_class = Client
|
||||
|
||||
# Base setup
|
||||
super().setup(url, auth_flow, api_key, **kwargs)
|
||||
|
||||
def disconnect(self):
|
||||
"""
|
||||
Close the client and clear the cookies.
|
||||
:return:
|
||||
"""
|
||||
self.client.close()
|
||||
self.cookies.clear()
|
||||
self.is_logged_in = False
|
||||
logger.info("Client closed")
|
||||
|
||||
def login(self):
|
||||
|
||||
if not self.is_ready():
|
||||
raise ApiException(
|
||||
"Client is not yet set up." "Please call 'connect()' first."
|
||||
)
|
||||
|
||||
# Log the call for debugging purposes
|
||||
logger.debug(
|
||||
f"API call to: {self._url_auth_login}",
|
||||
extra={
|
||||
"method": "get",
|
||||
"auth_flow": self.auth_flow,
|
||||
"timeout": self.client.timeout,
|
||||
"ignore_ssl": self._ignore_ssl,
|
||||
"call_count": self._call_count,
|
||||
},
|
||||
)
|
||||
|
||||
# Make the login call
|
||||
try:
|
||||
response = self.client.get(self._url_auth_login)
|
||||
response.raise_for_status()
|
||||
content = response.json()
|
||||
if content.get("flow", False) == "login":
|
||||
if "user_id" in content.keys():
|
||||
self.user_id = content["user_id"]
|
||||
if "contact_id" in content.keys():
|
||||
self.contact_id = content["contact_id"]
|
||||
logger.info("Login successful.")
|
||||
self.is_logged_in = True
|
||||
else:
|
||||
m = "Authentication failed."
|
||||
logger.exception(m, exc_info=True)
|
||||
raise ApiAuthError(m)
|
||||
|
||||
# Catch errors
|
||||
except Exception as e:
|
||||
m = f"Login failed: {e}"
|
||||
logger.exception(m, exc_info=True)
|
||||
raise ApiAuthError(str(e))
|
||||
|
||||
finally:
|
||||
self._call_count += 1
|
||||
|
||||
def _send_request(self, request: Request):
|
||||
"""
|
||||
Synchronous implementation of sending a request
|
||||
:param request:
|
||||
:return: Response
|
||||
"""
|
||||
try:
|
||||
result = self.client.send(request)
|
||||
|
||||
# Catch exceptions
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
raise ApiError(str(e))
|
||||
|
||||
# Increment call count
|
||||
finally:
|
||||
self._call_count += 1
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class AsyncCiviCrmApi(BaseCiviCrmApi):
|
||||
"""
|
||||
This class provides an async interface to CiviCRM APIv4.
|
||||
In most cases, you should not instantiate this class directly but use the
|
||||
global async_api instance from the package.
|
||||
"""
|
||||
|
||||
def setup(
|
||||
self,
|
||||
url: str,
|
||||
auth_flow: AuthFlow,
|
||||
api_key: str,
|
||||
**kwargs,
|
||||
):
|
||||
self._client_class = AsyncClient
|
||||
|
||||
# Base setup
|
||||
super().setup(url, auth_flow, api_key, **kwargs)
|
||||
|
||||
async def disconnect(self):
|
||||
"""
|
||||
Close the client and clear the cookies.
|
||||
:return:
|
||||
"""
|
||||
await self.client.aclose()
|
||||
self.cookies.clear()
|
||||
self.is_logged_in = False
|
||||
logger.info("Client closed")
|
||||
|
||||
async def login(self):
|
||||
|
||||
if not self.is_ready():
|
||||
raise ApiException(
|
||||
"Client is not yet set up." "Please call 'connect()' first."
|
||||
)
|
||||
|
||||
# Log the call for debugging purposes
|
||||
logger.debug(
|
||||
f"API call to: {self._url_auth_login}",
|
||||
extra={
|
||||
"method": "get",
|
||||
"auth_flow": self.auth_flow,
|
||||
"timeout": self.client.timeout,
|
||||
"ignore_ssl": self._ignore_ssl,
|
||||
"call_count": self._call_count,
|
||||
},
|
||||
)
|
||||
|
||||
# Make the login call
|
||||
try:
|
||||
response = await self.client.get(self._url_auth_login)
|
||||
response.raise_for_status()
|
||||
content = await response.json()
|
||||
if content.get("flow", False) == "login":
|
||||
if "user_id" in content.keys():
|
||||
self.user_id = content["user_id"]
|
||||
if "contact_id" in content.keys():
|
||||
self.contact_id = content["contact_id"]
|
||||
logger.info("Login successful.")
|
||||
self.is_logged_in = True
|
||||
else:
|
||||
m = "Authentication failed."
|
||||
logger.exception(m)
|
||||
raise ApiAuthError(m)
|
||||
|
||||
# Catch errors
|
||||
except Exception as e:
|
||||
m = f"Login failed: {e}"
|
||||
logger.exception(m)
|
||||
raise ApiAuthError(str(e))
|
||||
|
||||
finally:
|
||||
self._call_count += 1
|
||||
|
||||
async def _send_request(self, request: Request):
|
||||
"""
|
||||
Asynchronous implementation of sending a request
|
||||
:param request:
|
||||
:return: Response
|
||||
"""
|
||||
try:
|
||||
result = await self.client.send(request)
|
||||
|
||||
# Catch exceptions
|
||||
except Exception as e:
|
||||
logger.exception(e, exc_info=True)
|
||||
raise ApiError(str(e))
|
||||
|
||||
# Increment call count
|
||||
finally:
|
||||
self._call_count += 1
|
||||
|
||||
return result
|
534
uv.lock
generated
Normal file
534
uv.lock
generated
Normal file
|
@ -0,0 +1,534 @@
|
|||
version = 1
|
||||
revision = 1
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[package]]
|
||||
name = "annotated-types"
|
||||
version = "0.7.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyio"
|
||||
version = "4.8.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "idna" },
|
||||
{ name = "sniffio" },
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a3/73/199a98fc2dae33535d6b8e8e6ec01f8c1d76c9adb096c6b7d64823038cde/anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a", size = 181126 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/46/eb/e7f063ad1fec6b3178a3cd82d1a3c4de82cccf283fc42746168188e1cdd5/anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a", size = 96041 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bandit"
|
||||
version = "1.8.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
{ name = "pyyaml" },
|
||||
{ name = "rich" },
|
||||
{ name = "stevedore" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1a/a5/144a45f8e67df9d66c3bc3f7e69a39537db8bff1189ab7cff4e9459215da/bandit-1.8.3.tar.gz", hash = "sha256:f5847beb654d309422985c36644649924e0ea4425c76dec2e89110b87506193a", size = 4232005 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/88/85/db74b9233e0aa27ec96891045c5e920a64dd5cbccd50f8e64e9460f48d35/bandit-1.8.3-py3-none-any.whl", hash = "sha256:28f04dc0d258e1dd0f99dee8eefa13d1cb5e3fde1a5ab0c523971f97b289bcd8", size = 129078 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "25.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "click" },
|
||||
{ name = "mypy-extensions" },
|
||||
{ name = "packaging" },
|
||||
{ name = "pathspec" },
|
||||
{ name = "platformdirs" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/83/71/3fe4741df7adf015ad8dfa082dd36c94ca86bb21f25608eb247b4afb15b2/black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b", size = 1650988 },
|
||||
{ url = "https://files.pythonhosted.org/packages/13/f3/89aac8a83d73937ccd39bbe8fc6ac8860c11cfa0af5b1c96d081facac844/black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc", size = 1453985 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/22/b99efca33f1f3a1d2552c714b1e1b5ae92efac6c43e790ad539a163d1754/black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f", size = 1783816 },
|
||||
{ url = "https://files.pythonhosted.org/packages/18/7e/a27c3ad3822b6f2e0e00d63d58ff6299a99a5b3aee69fa77cd4b0076b261/black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba", size = 1440860 },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673 },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926 },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613 },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bracex"
|
||||
version = "2.5.post1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d6/6c/57418c4404cd22fe6275b8301ca2b46a8cdaa8157938017a9ae0b3edf363/bracex-2.5.post1.tar.gz", hash = "sha256:12c50952415bfa773d2d9ccb8e79651b8cdb1f31a42f6091b804f6ba2b4a66b6", size = 26641 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/02/8db98cdc1a58e0abd6716d5e63244658e6e63513c65f469f34b6f1053fd0/bracex-2.5.post1-py3-none-any.whl", hash = "sha256:13e5732fec27828d6af308628285ad358047cec36801598368cb28bc631dbaf6", size = 11558 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bump-my-version"
|
||||
version = "1.0.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "click" },
|
||||
{ name = "httpx" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "pydantic-settings" },
|
||||
{ name = "questionary" },
|
||||
{ name = "rich" },
|
||||
{ name = "rich-click" },
|
||||
{ name = "tomlkit" },
|
||||
{ name = "wcmatch" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/61/c9/22f5e6de03ec21357fd37e61fad2970043c406a9af217a0bfc68747148d8/bump_my_version-1.0.2.tar.gz", hash = "sha256:2f156877d2cdcda69afcb257ae4564c26e70f2fd5e5b15f2c7f26ab9e91502da", size = 1102688 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/ce/dc13887c45dead36075a210487ff66304ef0dc3fbc997d2b12bcde2f0401/bump_my_version-1.0.2-py3-none-any.whl", hash = "sha256:61d350b8c71968dd4520fc6b9df8b982c7df254cd30858b8645eff0f4eaf380b", size = 58573 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2025.1.31"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "civifang"
|
||||
version = "0.2.5"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "httpx" },
|
||||
{ name = "validators" },
|
||||
]
|
||||
|
||||
[package.dev-dependencies]
|
||||
dev = [
|
||||
{ name = "bandit" },
|
||||
{ name = "black" },
|
||||
{ name = "bump-my-version" },
|
||||
{ name = "uv" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "httpx", specifier = ">=0.28.1" },
|
||||
{ name = "validators", specifier = ">=0.34.0" },
|
||||
]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "bandit", specifier = ">=1.8.3" },
|
||||
{ name = "black", specifier = ">=25.1.0" },
|
||||
{ name = "bump-my-version", specifier = ">=1.0.2" },
|
||||
{ name = "uv", specifier = ">=0.6.5" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.1.8"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h11"
|
||||
version = "0.14.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httpcore"
|
||||
version = "1.0.7"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "h11" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httpx"
|
||||
version = "0.28.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio" },
|
||||
{ name = "certifi" },
|
||||
{ name = "httpcore" },
|
||||
{ name = "idna" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.10"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markdown-it-py"
|
||||
version = "3.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "mdurl" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mdurl"
|
||||
version = "0.1.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "1.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "24.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "0.12.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pbr"
|
||||
version = "6.1.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "setuptools" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/01/d2/510cc0d218e753ba62a1bc1434651db3cd797a9716a0a66cc714cb4f0935/pbr-6.1.1.tar.gz", hash = "sha256:93ea72ce6989eb2eed99d0f75721474f69ad88128afdef5ac377eb797c4bf76b", size = 125702 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/47/ac/684d71315abc7b1214d59304e23a982472967f6bf4bde5a98f1503f648dc/pbr-6.1.1-py2.py3-none-any.whl", hash = "sha256:38d4daea5d9fa63b3f626131b9d34947fd0c8be9b05a29276870580050a25a76", size = 108997 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "4.3.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prompt-toolkit"
|
||||
version = "3.0.50"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "wcwidth" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a1/e1/bd15cb8ffdcfeeb2bdc215de3c3cffca11408d829e4b8416dcfe71ba8854/prompt_toolkit-3.0.50.tar.gz", hash = "sha256:544748f3860a2623ca5cd6d2795e7a14f3d0e1c3c9728359013f79877fc89bab", size = 429087 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/ea/d836f008d33151c7a1f62caf3d8dd782e4d15f6a43897f64480c2b8de2ad/prompt_toolkit-3.0.50-py3-none-any.whl", hash = "sha256:9b6427eb19e479d98acff65196a307c555eb567989e6d88ebbb1b509d9779198", size = 387816 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.10.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "annotated-types" },
|
||||
{ name = "pydantic-core" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b7/ae/d5220c5c52b158b1de7ca89fc5edb72f304a70a4c540c84c8844bf4008de/pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236", size = 761681 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/3c/8cc1cc84deffa6e25d2d0c688ebb80635dfdbf1dbea3e30c541c8cf4d860/pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584", size = 431696 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic-core"
|
||||
version = "2.27.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fc/01/f3e5ac5e7c25833db5eb555f7b7ab24cd6f8c322d3a3ad2d67a952dc0abc/pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39", size = 413443 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/74/51c8a5482ca447871c93e142d9d4a92ead74de6c8dc5e66733e22c9bba89/pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0", size = 1893127 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/f3/c97e80721735868313c58b89d2de85fa80fe8dfeeed84dc51598b92a135e/pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef", size = 1811340 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/91/840ec1375e686dbae1bd80a9e46c26a1e0083e1186abc610efa3d9a36180/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7", size = 1822900 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/31/4240bc96025035500c18adc149aa6ffdf1a0062a4b525c932065ceb4d868/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934", size = 1869177 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/20/02fbaadb7808be578317015c462655c317a77a7c8f0ef274bc016a784c54/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6", size = 2038046 },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/86/7f306b904e6c9eccf0668248b3f272090e49c275bc488a7b88b0823444a4/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c", size = 2685386 },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/f0/49129b27c43396581a635d8710dae54a791b17dfc50c70164866bbf865e3/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2", size = 1997060 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/0f/943b4af7cd416c477fd40b187036c4f89b416a33d3cc0ab7b82708a667aa/pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4", size = 2004870 },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/40/aea70b5b1a63911c53a4c8117c0a828d6790483f858041f47bab0b779f44/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3", size = 1999822 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/b3/807b94fd337d58effc5498fd1a7a4d9d59af4133e83e32ae39a96fddec9d/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4", size = 2130364 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/df/791c827cd4ee6efd59248dca9369fb35e80a9484462c33c6649a8d02b565/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57", size = 2158303 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/67/4e197c300976af185b7cef4c02203e175fb127e414125916bf1128b639a9/pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc", size = 1834064 },
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/ea/cd7209a889163b8dcca139fe32b9687dd05249161a3edda62860430457a5/pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9", size = 1989046 },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/49/c54baab2f4658c26ac633d798dab66b4c3a9bbf47cff5284e9c182f4137a/pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b", size = 1885092 },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/b1/9bc383f48f8002f99104e3acff6cba1231b29ef76cfa45d1506a5cad1f84/pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b", size = 1892709 },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/6c/e62b8657b834f3eb2961b49ec8e301eb99946245e70bf42c8817350cbefc/pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154", size = 1811273 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/15/52cfe49c8c986e081b863b102d6b859d9defc63446b642ccbbb3742bf371/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9", size = 1823027 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/1c/b6f402cfc18ec0024120602bdbcebc7bdd5b856528c013bd4d13865ca473/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9", size = 1868888 },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/7b/8cb75b66ac37bc2975a3b7de99f3c6f355fcc4d89820b61dffa8f1e81677/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1", size = 2037738 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/f1/786d8fe78970a06f61df22cba58e365ce304bf9b9f46cc71c8c424e0c334/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a", size = 2685138 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/74/d12b2cd841d8724dc8ffb13fc5cef86566a53ed358103150209ecd5d1999/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e", size = 1997025 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/6e/940bcd631bc4d9a06c9539b51f070b66e8f370ed0933f392db6ff350d873/pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4", size = 2004633 },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/cc/a46b34f1708d82498c227d5d80ce615b2dd502ddcfd8376fc14a36655af1/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27", size = 1999404 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/2d/c365cfa930ed23bc58c41463bae347d1005537dc8db79e998af8ba28d35e/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee", size = 2130130 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/d7/eb64d015c350b7cdb371145b54d96c919d4db516817f31cd1c650cae3b21/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1", size = 2157946 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/99/bddde3ddde76c03b65dfd5a66ab436c4e58ffc42927d4ff1198ffbf96f5f/pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130", size = 1834387 },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/47/82b5e846e01b26ac6f1893d3c5f9f3a2eb6ba79be26eef0b759b4fe72946/pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee", size = 1990453 },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size = 1885186 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic-settings"
|
||||
version = "2.8.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pydantic" },
|
||||
{ name = "python-dotenv" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/88/82/c79424d7d8c29b994fb01d277da57b0a9b09cc03c3ff875f9bd8a86b2145/pydantic_settings-2.8.1.tar.gz", hash = "sha256:d5c663dfbe9db9d5e1c646b2e161da12f0d734d422ee56f567d0ea2cee4e8585", size = 83550 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/53/a64f03044927dc47aafe029c42a5b7aabc38dfb813475e0e1bf71c4a59d0/pydantic_settings-2.8.1-py3-none-any.whl", hash = "sha256:81942d5ac3d905f7f3ee1a70df5dfb62d5569c12f51a5a647defc1c3d9ee2e9c", size = 30839 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.19.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "python-dotenv"
|
||||
version = "1.0.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "6.0.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 },
|
||||
{ url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "questionary"
|
||||
version = "2.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "prompt-toolkit" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a8/b8/d16eb579277f3de9e56e5ad25280fab52fc5774117fb70362e8c2e016559/questionary-2.1.0.tar.gz", hash = "sha256:6302cdd645b19667d8f6e6634774e9538bfcd1aad9be287e743d96cacaf95587", size = 26775 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/3f/11dd4cd4f39e05128bfd20138faea57bec56f9ffba6185d276e3107ba5b2/questionary-2.1.0-py3-none-any.whl", hash = "sha256:44174d237b68bc828e4878c763a9ad6790ee61990e0ae72927694ead57bab8ec", size = 36747 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rich"
|
||||
version = "13.9.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "markdown-it-py" },
|
||||
{ name = "pygments" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rich-click"
|
||||
version = "1.8.8"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "click" },
|
||||
{ name = "rich" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a6/7a/4b78c5997f2a799a8c5c07f3b2145bbcda40115c4d35c76fbadd418a3c89/rich_click-1.8.8.tar.gz", hash = "sha256:547c618dea916620af05d4a6456da797fbde904c97901f44d2f32f89d85d6c84", size = 39066 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/69/963f0bf44a654f6465bdb66fb5a91051b0d7af9f742b5bd7202607165036/rich_click-1.8.8-py3-none-any.whl", hash = "sha256:205aabd5a98e64ab2c105dee9e368be27480ba004c7dfa2accd0ed44f9f1550e", size = 35747 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "76.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/32/d2/7b171caf085ba0d40d8391f54e1c75a1cda9255f542becf84575cfd8a732/setuptools-76.0.0.tar.gz", hash = "sha256:43b4ee60e10b0d0ee98ad11918e114c70701bc6051662a9a675a0496c1a158f4", size = 1349387 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/37/66/d2d7e6ad554f3a7c7297c3f8ef6e22643ad3d35ef5c63bf488bc89f32f31/setuptools-76.0.0-py3-none-any.whl", hash = "sha256:199466a166ff664970d0ee145839f5582cb9bca7a0a3a2e795b6a9cb2308e9c6", size = 1236106 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sniffio"
|
||||
version = "1.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stevedore"
|
||||
version = "5.4.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pbr" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/28/3f/13cacea96900bbd31bb05c6b74135f85d15564fc583802be56976c940470/stevedore-5.4.1.tar.gz", hash = "sha256:3135b5ae50fe12816ef291baff420acb727fcd356106e3e9cbfa9e5985cd6f4b", size = 513858 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/45/8c4ebc0c460e6ec38e62ab245ad3c7fc10b210116cea7c16d61602aa9558/stevedore-5.4.1-py3-none-any.whl", hash = "sha256:d10a31c7b86cba16c1f6e8d15416955fc797052351a56af15e608ad20811fcfe", size = 49533 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tomlkit"
|
||||
version = "0.13.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b1/09/a439bec5888f00a54b8b9f05fa94d7f901d6735ef4e55dcec9bc37b5d8fa/tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79", size = 192885 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/b6/a447b5e4ec71e13871be01ba81f5dfc9d0af7e473da256ff46bc0e24026f/tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde", size = 37955 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.12.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uv"
|
||||
version = "0.6.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5f/3b/9a699194b132948b377272f06f2218d6453d440c8bae77275cd7d21e64dc/uv-0.6.5.tar.gz", hash = "sha256:70ad4cc5f2b636edbeeebb3aee0a7daa66b17457038088be870ac7adc5a9842d", size = 3093602 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/40/ac0b8050e145ae7dab029e8590046f9f96af4c6a36a4c4ee328a81e56062/uv-0.6.5-py3-none-linux_armv6l.whl", hash = "sha256:c5676fc7cdd088e2c3342593c1d2dc379bf86a83301af7b0dfe8d45801a50d85", size = 15517362 },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/f8/c0c2a2d5021904830d0d9fac4885819d731af2ed8e4ec11d80751420c646/uv-0.6.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6e82de1cb6a116f7736de9542430d78c210d152c80723db8beffc14e5b4e4b40", size = 15606625 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/f7/1c5a44233ba80938b316eb67b6f3087a5cdc032882fbb86abfb7b8d14f3a/uv-0.6.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:442639a874f6bb6864279f099c97739287d7e244bc25d0f791345cc69f46c940", size = 14483413 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/15/68beb9094e976c9403d8b79de76601f793250d0ecb84cb69d5940ba36729/uv-0.6.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:b5445a509f500bbf18faba4e7cf5cc9763617c335d58afaa5f3e5a6e388dd4ee", size = 14914536 },
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/49/42d917ec3a6d79751d54862ac8d5170b1f509680bcad506d949f5d365aaa/uv-0.6.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c5683bccfc2b92cdc2f91e2904baa8ee2b5893b33ac8acac25e702ce7d3e5415", size = 15264210 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/4c/446c039726dc6f04cd963f2a0813ec4e35c57d3566a9daf0272e2c5e311d/uv-0.6.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43847ef95d56c239de940339e5cfc2ade58249005e8ab97244fdb69fb9761572", size = 15974263 },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/c6/46f5334c73846bb9afd883ca9a1f41262d677a3ee0e3ff0063acef5a8a05/uv-0.6.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ef0b2f810d87aa9bbad15c3ab113e555871f14c9cd8ad2205338fb0358aaf52d", size = 16842142 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/b4/b01cfa179b6e65aeb58eaf89bd3a6880082ec0fa391f93cc786db65ace03/uv-0.6.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9b56fa88951fab3bc7164255d844de9ad048e6a04a95f1c2774637e06889efe6", size = 16539261 },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/cc/1e00721e749ecc4d2cf8d233a9f9585108afcd62e3da4a2784f15d1f3a65/uv-0.6.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f3b3f21b2385545f499f6cc21f44eac3bbb0f6cb98fbf9c6d3e58db186c8a41", size = 20699878 },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/32/ad9944c9716360c82fb62516aca72bdeaedf7991483383f3a06734cb2cf4/uv-0.6.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15dae245979add192c4845947da1a9141f95c19403d1c0d75019182e6882e7d4", size = 16249288 },
|
||||
{ url = "https://files.pythonhosted.org/packages/80/7a/cad1a0382182b923f881ec9b592106abb0df55be42384bfbe3694fb5b243/uv-0.6.5-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:26a90e69d6438de2ec03ab452cc48d1cb375249c6b6980f4ed177f324a5ad8b3", size = 15156024 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/f1/a9721cf48232ee4d0871c74dbc7ff5e2e90fb79aab4096c76f12eb3ba453/uv-0.6.5-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:776500595ff7cda1ffa5a76dd3ff9de3819f1e26c493938cbdc20c1ab766b2eb", size = 15213625 },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/a4/02a2e4eb1a09b87086c92ebeb9953dca427b54ec113be2e0445abc850b3c/uv-0.6.5-py3-none-musllinux_1_1_i686.whl", hash = "sha256:6210fe6ef6a0ae3dc618611fcc8ada4e620fea5172fb8a9c50d3a59b6915b023", size = 15558969 },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/c1/5a3a0905a630a5b99b7b3cc1a400bcb65401e1a325bf43ced50e8bd007a2/uv-0.6.5-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:d47b4adffcdbe30bd678c7670e63c671b8b34a667898501e588f2e7cbce34656", size = 16345448 },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/80/721c0621f14071462bc8420b16c4cba3c9c066f5775eab7dc56d9b559c30/uv-0.6.5-py3-none-win32.whl", hash = "sha256:23aa8e8ca7795f54f6cf0f0fbd0aaf7b26bf4aae42f8c10643bcba6d42485a3f", size = 15657842 },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/d1/751610f12b99ab6166887554cd98d376f22ffb6fdc69e57676735e781ccc/uv-0.6.5-py3-none-win_amd64.whl", hash = "sha256:5323e9219a519c6553111820a8c54588d426380404a208b23cf4c3265bc87ec6", size = 16958031 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/63/0080e1618c936297001a3da462dd83f73391bacf7857ed7b327518d57f93/uv-0.6.5-py3-none-win_arm64.whl", hash = "sha256:a481254f63240023239ecec80cd690dec05875e248eb4b9d7f66957b017798b1", size = 15811982 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "validators"
|
||||
version = "0.34.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/64/07/91582d69320f6f6daaf2d8072608a4ad8884683d4840e7e4f3a9dbdcc639/validators-0.34.0.tar.gz", hash = "sha256:647fe407b45af9a74d245b943b18e6a816acf4926974278f6dd617778e1e781f", size = 70955 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/78/36828a4d857b25896f9774c875714ba4e9b3bc8a92d2debe3f4df3a83d4f/validators-0.34.0-py3-none-any.whl", hash = "sha256:c804b476e3e6d3786fa07a30073a4ef694e617805eb1946ceee3fe5a9b8b1321", size = 43536 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wcmatch"
|
||||
version = "10.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "bracex" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/41/ab/b3a52228538ccb983653c446c1656eddf1d5303b9cb8b9aef6a91299f862/wcmatch-10.0.tar.gz", hash = "sha256:e72f0de09bba6a04e0de70937b0cf06e55f36f37b3deb422dfaf854b867b840a", size = 115578 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/df/4ee467ab39cc1de4b852c212c1ed3becfec2e486a51ac1ce0091f85f38d7/wcmatch-10.0-py3-none-any.whl", hash = "sha256:0dd927072d03c0a6527a20d2e6ad5ba8d0380e60870c383bc533b71744df7b7a", size = 39347 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wcwidth"
|
||||
version = "0.2.13"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 },
|
||||
]
|
Loading…
Add table
Add a link
Reference in a new issue