From 70720808cd958e6058129285d29fe9050c67fd0a Mon Sep 17 00:00:00 2001 From: Ralf Grubenmann Date: Wed, 20 Dec 2023 09:33:15 +0100 Subject: [PATCH 01/15] chore(service): return cli version in core svc (#3655) --- renku/ui/service/controllers/version.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/renku/ui/service/controllers/version.py b/renku/ui/service/controllers/version.py index f33e437ed7..f7f699e523 100644 --- a/renku/ui/service/controllers/version.py +++ b/renku/ui/service/controllers/version.py @@ -14,6 +14,8 @@ # See the License for the specific language governing permissions and # limitations under the License. """Renku service version controller.""" +import os + from renku import __version__ from renku.core.migration.migrate import SUPPORTED_PROJECT_VERSION from renku.ui.service.controllers.api.abstract import ServiceCtrl @@ -33,6 +35,7 @@ def to_response(self, minimum_version, maximum_version): { "latest_version": __version__, "supported_project_version": SUPPORTED_PROJECT_VERSION, + "cli_version": os.environ.get("RENKU_PROJECT_DEFAULT_CLI_VERSION") or __version__, "minimum_api_version": minimum_version.name, "maximum_api_version": maximum_version.name, }, From ec1e2824e6604cd82e677fdfb5e70d3491b7df68 Mon Sep 17 00:00:00 2001 From: Ralf Grubenmann Date: Wed, 20 Dec 2023 18:03:36 +0100 Subject: [PATCH 02/15] fix(cli): use lower case image names for sessions in upper-case projects (#3666) --- renku/core/session/session.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/renku/core/session/session.py b/renku/core/session/session.py index 8b0773926e..de5daf6acd 100644 --- a/renku/core/session/session.py +++ b/renku/core/session/session.py @@ -179,9 +179,11 @@ def session_start( if image_name is None: tag = project_context.repository.head.commit.hexsha[:7] repo_host = get_image_repository_host() - image_name = f"{project_name}:{tag}" + image_name = f"{project_name.lower()}:{tag}" if repo_host: image_name = f"{repo_host}/{image_name}" + if image_name.lower() != image_name: + raise errors.SessionStartError(f"Image name '{image_name}' cannot contain upper-case letters.") force_build_image = provider_api.force_build_image(**kwargs) From 8afaedddba248a34a0bb175189f04cf1119034e6 Mon Sep 17 00:00:00 2001 From: Ralf Grubenmann Date: Thu, 21 Dec 2023 10:38:58 +0100 Subject: [PATCH 03/15] fix(service): add proper error if a dataset can't be found (#3661) --- renku/ui/service/errors.py | 15 ++++++++++++++- renku/ui/service/views/error_handlers.py | 4 ++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/renku/ui/service/errors.py b/renku/ui/service/errors.py index 961718bf9c..1504a89d5d 100644 --- a/renku/ui/service/errors.py +++ b/renku/ui/service/errors.py @@ -121,7 +121,7 @@ def _handle_sentry(self): sentry_target = sentry_url.netloc.split("@")[-1] # NOTE: sentry doesn't support a global search. A proper link would require the specific org sentry = f"{sentry_url.scheme }://{sentry_target}/organizations/sentry?query={sentry}" - except Exception: + except Exception: # nosec pass except KeyError as e: sentry = f"Unexpected error while reporting to Sentry: {str(e)}" @@ -348,6 +348,19 @@ def __init__(self, exception=None): super().__init__(exception=exception) +class UserDatasetsNotFoundError(ServiceError): + """Dataset couldn't be found in project.""" + + code = SVC_ERROR_USER + 133 + userMessage = ( + "The dataset doesn't exist in the project. Please check your inputs or create the target dataset first." + ) + devMessage = "The dataset couldn't be found in the project." + + def __init__(self, exception=None): + super().__init__(exception=exception) + + class UserOutdatedProjectError(ServiceError): """The operation can be done only after updating the target project.""" diff --git a/renku/ui/service/views/error_handlers.py b/renku/ui/service/views/error_handlers.py index 5cfab9e0bc..d56de3bbfc 100644 --- a/renku/ui/service/views/error_handlers.py +++ b/renku/ui/service/views/error_handlers.py @@ -28,6 +28,7 @@ AuthenticationError, DatasetExistsError, DatasetImageError, + DatasetNotFound, DockerfileUpdateError, GitCommandError, GitError, @@ -65,6 +66,7 @@ ProgramUpdateProjectError, ServiceError, UserDatasetsMultipleImagesError, + UserDatasetsNotFoundError, UserDatasetsUnlinkError, UserDatasetsUnreachableImageError, UserInvalidGenericFieldsError, @@ -368,6 +370,8 @@ def decorated_function(*args, **kwargs): if "".join(value) == "Field may not be null.": raise UserMissingFieldError(e, key) raise + except DatasetNotFound as e: + raise UserDatasetsNotFoundError(e) except DatasetExistsError as e: raise IntermittentDatasetExistsError(e) except RenkuException as e: From 5954aacac6ab724a14458957ccc6e64137632592 Mon Sep 17 00:00:00 2001 From: Ralf Grubenmann Date: Wed, 3 Jan 2024 16:02:49 +0100 Subject: [PATCH 04/15] fix: prevent distutils warning (#3663) --- pyproject.toml | 1 + renku/__init__.py | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 6bfbfebeb8..6392863199 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -314,6 +314,7 @@ module = [ "ruamel", "rq", "shellingham", + "setuptools", "toil.*", "tqdm", "urllib3.*", diff --git a/renku/__init__.py b/renku/__init__.py index e2238061c2..516fde5906 100644 --- a/renku/__init__.py +++ b/renku/__init__.py @@ -23,6 +23,14 @@ from renku.version import __template_version__, __version__ +# distutils is deprecated and fully replaced by setuptools. we don't depend on either, but some of our +# dependencies do and if distutils gets imported before setuptools, we get an annoying warning. +# By forcing the import here first, we prevent that warning and ensure the setuptools version is used. +try: + import setuptools # noqa: F401 # type: ignore +except ImportError: + pass + class LoaderWrapper(importlib.abc.Loader): """Wrap an importlib loader and add the loaded module to sys.modules with an additional name.""" From d74cc72467ce10d9330e4080aa3540697e6a2869 Mon Sep 17 00:00:00 2001 From: Ralf Grubenmann Date: Wed, 3 Jan 2024 20:24:01 +0100 Subject: [PATCH 05/15] fix(service): allow editing datasets without creator email (#3664) --- renku/ui/service/controllers/datasets_edit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/renku/ui/service/controllers/datasets_edit.py b/renku/ui/service/controllers/datasets_edit.py index 355c1efe36..74f4eaa3f5 100644 --- a/renku/ui/service/controllers/datasets_edit.py +++ b/renku/ui/service/controllers/datasets_edit.py @@ -73,7 +73,7 @@ def renku_op(self): creators: Union[NoValueType, List[Person]] if "creators" in self.ctx: - creators, warnings = construct_creators(self.ctx.get("creators")) # type: ignore + creators, warnings = construct_creators(self.ctx.get("creators"), ignore_email=True) # type: ignore else: creators = NO_VALUE From 12469f913d4a80662c2b7e2fe2e3f9f594900078 Mon Sep 17 00:00:00 2001 From: Ralf Grubenmann Date: Thu, 4 Jan 2024 09:27:30 +0100 Subject: [PATCH 06/15] fix(cli): output proper session link and only check registry if logged in (#3660) --- renku/core/session/docker.py | 41 ++++++++++++++++++++++++++++++------ renku/core/session/utils.py | 6 ++++++ 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/renku/core/session/docker.py b/renku/core/session/docker.py index f7a930ef42..129e9e93bb 100644 --- a/renku/core/session/docker.py +++ b/renku/core/session/docker.py @@ -20,7 +20,7 @@ import webbrowser from datetime import datetime from pathlib import Path -from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Tuple, Union, cast +from typing import TYPE_CHECKING, Any, Dict, Iterator, List, Optional, Tuple, Union, cast from uuid import uuid4 import docker @@ -29,8 +29,11 @@ from renku.core import errors from renku.core.config import get_value from renku.core.constant import ProviderPriority +from renku.core.login import read_renku_token from renku.core.plugin import hookimpl +from renku.core.session.utils import get_renku_url from renku.core.util import communication +from renku.core.util.jwt import is_token_expired from renku.domain_model.project_context import project_context from renku.domain_model.session import ISessionProvider, Session, SessionStopStatus @@ -67,11 +70,14 @@ def docker_client(self) -> docker.client.DockerClient: return self._docker_client @staticmethod - def _get_jupyter_urls(ports: Dict[str, Any], auth_token: str, jupyter_port: int = 8888) -> Iterable[str]: + def _get_jupyter_urls(ports: Dict[str, Any], auth_token: str, jupyter_port: int = 8888) -> Iterator[str]: port_key = f"{jupyter_port}/tcp" if port_key not in ports: - return list() - return map(lambda x: f"http://{x['HostIp']}:{x['HostPort']}/?token={auth_token}", ports[port_key]) + return list() # type: ignore + default_url = get_value("interactive", "default_url") + if not default_url: + default_url = "/lab" + return map(lambda x: f"http://{x['HostIp']}:{x['HostPort']}{default_url}?token={auth_token}", ports[port_key]) def _get_docker_containers(self, project_name: str) -> List[docker.models.containers.Container]: return self.docker_client().containers.list(filters={"label": f"renku_project={project_name}"}) @@ -92,9 +98,22 @@ def build_image(self, image_descriptor: Path, image_name: str, config: Optional[ def find_image(self, image_name: str, config: Optional[Dict[str, Any]]) -> bool: """Find the given container image.""" with communication.busy(msg=f"Checking for image {image_name}"): + renku_url = get_renku_url() + + # only search remote image if a user is logged in + find_remote = True + if renku_url is None: + find_remote = False + else: + token = read_renku_token(endpoint=renku_url) + if not token or is_token_expired(token): + find_remote = False + try: self.docker_client().images.get(image_name) except docker.errors.ImageNotFound: + if not find_remote: + return False try: self.docker_client().images.get_registry_data(image_name) except docker.errors.APIError: @@ -454,13 +473,23 @@ def session_open(self, project_name: str, session_name: Optional[str], **kwargs) def session_url(self, session_name: Optional[str]) -> Optional[str]: """Get the URL of the interactive session.""" sessions = self.docker_client().containers.list() + default_url = get_value("interactive", "default_url") + if not default_url: + default_url = "/lab" for c in sessions: if ( c.short_id == session_name or (not session_name and len(sessions) == 1) ) and f"{DockerSessionProvider.JUPYTER_PORT}/tcp" in c.ports: - host = c.ports[f"{DockerSessionProvider.JUPYTER_PORT}/tcp"][0] - return f'http://{host["HostIp"]}:{host["HostPort"]}/?token={c.labels["jupyter_token"]}' + url = next( + DockerSessionProvider._get_jupyter_urls( + c.ports, c.labels["jupyter_token"], DockerSessionProvider.JUPYTER_PORT + ), + None, + ) + if not url: + continue + return url return None def force_build_image(self, force_build: bool = False, **kwargs) -> bool: diff --git a/renku/core/session/utils.py b/renku/core/session/utils.py index c6f16de97c..078271a9d2 100644 --- a/renku/core/session/utils.py +++ b/renku/core/session/utils.py @@ -18,7 +18,9 @@ import urllib from typing import Optional +from renku.core.login import read_renku_token from renku.core.util.git import get_remote +from renku.core.util.jwt import is_token_expired from renku.core.util.urls import parse_authentication_endpoint from renku.domain_model.project_context import project_context @@ -54,4 +56,8 @@ def get_image_repository_host() -> Optional[str]: renku_url = get_renku_url() if not renku_url: return None + token = read_renku_token(endpoint=renku_url) + if not token or is_token_expired(token): + # only guess host if user is logged in + return None return "registry." + urllib.parse.urlparse(renku_url).netloc From 9377ac4ab55f778c4ef1b505f4baeb0bc2378ebd Mon Sep 17 00:00:00 2001 From: Ralf Grubenmann Date: Thu, 4 Jan 2024 20:39:15 +0100 Subject: [PATCH 07/15] fix(service): allow setting keywords on project creation (#3665) --- renku/ui/service/controllers/templates_create_project.py | 1 + tests/service/views/test_templates_views.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/renku/ui/service/controllers/templates_create_project.py b/renku/ui/service/controllers/templates_create_project.py index d01220d7a7..0b988f47d1 100644 --- a/renku/ui/service/controllers/templates_create_project.py +++ b/renku/ui/service/controllers/templates_create_project.py @@ -182,6 +182,7 @@ def new_project(self): data_dir=self.ctx.get("data_directory"), ssh_supported=self.template.ssh_supported, image_request=image, + keywords=self.ctx.get("project_keywords", []), ) self.new_project_push(new_project_path) diff --git a/tests/service/views/test_templates_views.py b/tests/service/views/test_templates_views.py index 4b28eae15f..d12309ac91 100644 --- a/tests/service/views/test_templates_views.py +++ b/tests/service/views/test_templates_views.py @@ -134,6 +134,7 @@ def test_create_project_from_template(svc_client_templates_creation, with_inject "content_url": "https://en.wikipedia.org/static/images/icons/wikipedia.png", "mirror_locally": True, } + payload["project_keywords"] = ["test", "ci"] response = svc_client.post("/templates.create_project", data=json.dumps(payload), headers=headers) @@ -157,6 +158,7 @@ def test_create_project_from_template(svc_client_templates_creation, with_inject with with_injection(): project = project_context.project assert project_context.datadir == "my-folder/" + assert project.keywords == ["test", "ci"] expected_id = f"/projects/{payload['project_namespace']}/{stripped_name}" assert expected_id == project.id From 77f480f824cc26b218b29f1260115634c29dcd70 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 11 Jan 2024 11:29:40 +0100 Subject: [PATCH 08/15] chore: combined dependency update (#3672) bump dependencies, fix gitpython security advisory --- .github/actions/install-linux/action.yml | 4 +- .github/actions/install-macos/action.yml | 4 +- poetry.lock | 1429 +++++++++++----------- pyproject.toml | 8 +- 4 files changed, 716 insertions(+), 729 deletions(-) diff --git a/.github/actions/install-linux/action.yml b/.github/actions/install-linux/action.yml index 0f5d857cf8..5aca43c515 100644 --- a/.github/actions/install-linux/action.yml +++ b/.github/actions/install-linux/action.yml @@ -4,11 +4,11 @@ inputs: python-version: description: "Python version to install" required: false - default: "3.9" + default: "3.10" runs: using: "composite" steps: - - uses: actions/checkout@v3.5.0 + - uses: actions/checkout@v3.6.0 with: fetch-depth: 0 - name: Checkout repository diff --git a/.github/actions/install-macos/action.yml b/.github/actions/install-macos/action.yml index ac96da4e04..a3dd40fa7f 100644 --- a/.github/actions/install-macos/action.yml +++ b/.github/actions/install-macos/action.yml @@ -4,11 +4,11 @@ inputs: python-version: description: "Python version to install" required: false - default: "3.9" + default: "3.10" runs: using: "composite" steps: - - uses: actions/checkout@v3.5.0 + - uses: actions/checkout@v3.6.0 with: fetch-depth: 0 - name: Checkout repository diff --git a/poetry.lock b/poetry.lock index 847eff8908..45e241ca3e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. [[package]] name = "addict" @@ -49,13 +49,13 @@ files = [ [[package]] name = "apispec" -version = "6.3.0" +version = "6.3.1" description = "A pluggable API specification generator. Currently supports the OpenAPI Specification (f.k.a. the Swagger specification)." optional = true python-versions = ">=3.7" files = [ - {file = "apispec-6.3.0-py3-none-any.whl", hash = "sha256:95a0b9355785df998bb0e9b939237a30ee4c7428fd6ef97305eae3da06b9b339"}, - {file = "apispec-6.3.0.tar.gz", hash = "sha256:6cb08d92ce73ff0b3bf46cb2ea5c00d57289b0f279fb0256a3df468182ba5344"}, + {file = "apispec-6.3.1-py3-none-any.whl", hash = "sha256:c7f31da97c966bc94406622d4b8d0d769947a142b52e487765ea366f916bc273"}, + {file = "apispec-6.3.1.tar.gz", hash = "sha256:b38e4479916d43f2b1e88ce15fc2fae93adf2e8d55cb59ec74ac66a827941483"}, ] [package.dependencies] @@ -63,12 +63,12 @@ packaging = ">=21.3" PyYAML = {version = ">=3.10", optional = true, markers = "extra == \"yaml\""} [package.extras] -dev = ["PyYAML (>=3.10)", "flake8 (==5.0.4)", "flake8-bugbear (==22.9.23)", "marshmallow (>=3.13.0)", "mypy (==0.982)", "openapi-spec-validator (<0.5)", "prance[osv] (>=0.11)", "pre-commit (>=2.4,<3.0)", "pytest", "tox", "types-PyYAML"] +dev = ["PyYAML (>=3.10)", "flake8 (==5.0.4)", "flake8-bugbear (==22.9.23)", "marshmallow (>=3.13.0)", "mypy (==0.982)", "prance[osv] (>=0.11)", "pre-commit (>=2.4,<3.0)", "pytest", "tox", "types-PyYAML"] docs = ["marshmallow (>=3.13.0)", "pyyaml (==6.0)", "sphinx (==5.2.3)", "sphinx-issues (==3.0.1)", "sphinx-rtd-theme (==1.0.0)"] lint = ["flake8 (==5.0.4)", "flake8-bugbear (==22.9.23)", "mypy (==0.982)", "pre-commit (>=2.4,<3.0)", "types-PyYAML"] marshmallow = ["marshmallow (>=3.18.0)"] -tests = ["PyYAML (>=3.10)", "marshmallow (>=3.13.0)", "openapi-spec-validator (<0.5)", "prance[osv] (>=0.11)", "pytest"] -validation = ["openapi-spec-validator (<0.5)", "prance[osv] (>=0.11)"] +tests = ["PyYAML (>=3.10)", "marshmallow (>=3.13.0)", "prance[osv] (>=0.11)", "pytest"] +validation = ["prance[osv] (>=0.11)"] yaml = ["PyYAML (>=3.10)"] [[package]] @@ -108,13 +108,13 @@ tests = ["Flask (==1.1.1)", "bottle (==0.12.17)", "mock", "pytest", "tornado"] [[package]] name = "argcomplete" -version = "3.1.6" +version = "3.2.1" description = "Bash tab completion for argparse" optional = false python-versions = ">=3.8" files = [ - {file = "argcomplete-3.1.6-py3-none-any.whl", hash = "sha256:71f4683bc9e6b0be85f2b2c1224c47680f210903e23512cfebfe5a41edfd883a"}, - {file = "argcomplete-3.1.6.tar.gz", hash = "sha256:3b1f07d133332547a53c79437527c00be48cca3807b1d4ca5cab1b26313386a6"}, + {file = "argcomplete-3.2.1-py3-none-any.whl", hash = "sha256:30891d87f3c1abe091f2142613c9d33cac84a5e15404489f033b20399b691fec"}, + {file = "argcomplete-3.2.1.tar.gz", hash = "sha256:437f67fb9b058da5a090df505ef9be0297c4883993f3f56cb186ff087778cfb4"}, ] [package.extras] @@ -133,31 +133,32 @@ files = [ [[package]] name = "attrs" -version = "23.1.0" +version = "23.2.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" files = [ - {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, - {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, ] [package.extras] cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[docs,tests]", "pre-commit"] +dev = ["attrs[tests]", "pre-commit"] docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] [[package]] name = "babel" -version = "2.13.1" +version = "2.14.0" description = "Internationalization utilities" optional = false python-versions = ">=3.7" files = [ - {file = "Babel-2.13.1-py3-none-any.whl", hash = "sha256:7077a4984b02b6727ac10f1f7294484f737443d7e2e66c5e4380e41a3ae0b4ed"}, - {file = "Babel-2.13.1.tar.gz", hash = "sha256:33e0952d7dd6374af8dbf6768cc4ddf3ccfefc244f9986d4074704f2fbd18900"}, + {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, + {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, ] [package.dependencies] @@ -752,13 +753,13 @@ test-randomorder = ["pytest-randomly"] [[package]] name = "cwl-upgrader" -version = "1.2.10" -description = "Common Workflow Language standalone document upgrader" +version = "1.2.11" +description = "Upgrade a CWL tool or workflow document from one version to another" optional = false -python-versions = ">=3.8, <4" +python-versions = ">=3.8" files = [ - {file = "cwl-upgrader-1.2.10.tar.gz", hash = "sha256:d0a4b2443a654819613a62f7214d0e28a52fcbc71942c71c18ccdd2fde35038b"}, - {file = "cwl_upgrader-1.2.10-py3-none-any.whl", hash = "sha256:0946ffd5b338d0ec738d8b1a2d2de2f797cc34b4b3c09c6b164eb42ea1423e2b"}, + {file = "cwl-upgrader-1.2.11.tar.gz", hash = "sha256:57b4b6fea246ae96ac28dc4e65b28899e50a8be430957fbd97f25bbe2dfa468a"}, + {file = "cwl_upgrader-1.2.11-py3-none-any.whl", hash = "sha256:435b706b7ccc64c6b0af6785a0cebbdfa285af9e70f321dc7b43b363ba9b385c"}, ] [package.dependencies] @@ -766,6 +767,9 @@ files = [ schema-salad = "*" setuptools = "*" +[package.extras] +testing = ["pytest (<8)"] + [[package]] name = "cwl-utils" version = "0.27" @@ -814,8 +818,8 @@ pyparsing = "!=3.0.2" rdflib = ">=4.2.2,<6.4.0" requests = ">=2.6.1" "ruamel.yaml" = [ - {version = ">=0.16.0,<0.17.28", markers = "python_version >= \"3.10\""}, {version = ">=0.15,<0.17.28", markers = "python_version < \"3.10\""}, + {version = ">=0.16.0,<0.17.28", markers = "python_version >= \"3.10\""}, ] schema-salad = ">=8.4,<9" setuptools = "*" @@ -863,13 +867,13 @@ optimize = ["orjson"] [[package]] name = "deepmerge" -version = "1.1.0" +version = "1.1.1" description = "a toolset to deeply merge python dictionaries." optional = false python-versions = "*" files = [ - {file = "deepmerge-1.1.0-py3-none-any.whl", hash = "sha256:59e6ef80b77dc52af3882a1ea78da22bcfc91ae9cdabc0c80729049fe295ff8b"}, - {file = "deepmerge-1.1.0.tar.gz", hash = "sha256:4c27a0db5de285e1a7ceac7dbc1531deaa556b627dea4900c8244581ecdfea2d"}, + {file = "deepmerge-1.1.1-py3-none-any.whl", hash = "sha256:7219dad9763f15be9dcd4bcb53e00f48e4eed6f5ed8f15824223eb934bb35977"}, + {file = "deepmerge-1.1.1.tar.gz", hash = "sha256:53a489dc9449636e480a784359ae2aab3191748c920649551c8e378622f0eca4"}, ] [[package]] @@ -888,13 +892,13 @@ graph = ["objgraph (>=1.7.2)"] [[package]] name = "distlib" -version = "0.3.7" +version = "0.3.8" description = "Distribution utilities" optional = false python-versions = "*" files = [ - {file = "distlib-0.3.7-py2.py3-none-any.whl", hash = "sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057"}, - {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, ] [[package]] @@ -944,13 +948,13 @@ packaging = ">=20.9" [[package]] name = "enlighten" -version = "1.12.2" +version = "1.12.4" description = "Enlighten Progress Bar" optional = false python-versions = "*" files = [ - {file = "enlighten-1.12.2-py2.py3-none-any.whl", hash = "sha256:14ed5ea210cd77c5f11a84749adf2d3557b86451ed4f12ed0d2d613257fc1437"}, - {file = "enlighten-1.12.2.tar.gz", hash = "sha256:8c09f6571119746cee6776cbf4e7c04a48e888f4ccae6db377a5876741f0899f"}, + {file = "enlighten-1.12.4-py2.py3-none-any.whl", hash = "sha256:5c53c57441bc5986c1d02f2f539aead9d59a206783641953a49b8d995db6b584"}, + {file = "enlighten-1.12.4.tar.gz", hash = "sha256:75f3d92b49e0ef5e454fc1a0f39dc0ab8f6d9946cbe534db3ded3010217d5b5f"}, ] [package.dependencies] @@ -987,13 +991,13 @@ testing = ["hatch", "pre-commit", "pytest", "tox"] [[package]] name = "fakeredis" -version = "2.20.0" +version = "2.20.1" description = "Python implementation of redis API, can be used for testing purposes." optional = false python-versions = ">=3.7,<4.0" files = [ - {file = "fakeredis-2.20.0-py3-none-any.whl", hash = "sha256:c9baf3c7fd2ebf40db50db4c642c7c76b712b1eed25d91efcc175bba9bc40ca3"}, - {file = "fakeredis-2.20.0.tar.gz", hash = "sha256:69987928d719d1ae1665ae8ebb16199d22a5ebae0b7d0d0d6586fc3a1a67428c"}, + {file = "fakeredis-2.20.1-py3-none-any.whl", hash = "sha256:d1cb22ed76b574cbf807c2987ea82fc0bd3e7d68a7a1e3331dd202cc39d6b4e5"}, + {file = "fakeredis-2.20.1.tar.gz", hash = "sha256:a2a5ccfcd72dc90435c18cde284f8cdd0cb032eb67d59f3fed907cde1cbffbbd"}, ] [package.dependencies] @@ -1102,48 +1106,47 @@ dotenv = ["python-dotenv"] [[package]] name = "frozendict" -version = "2.3.10" +version = "2.4.0" description = "A simple immutable dictionary" optional = false python-versions = ">=3.6" files = [ - {file = "frozendict-2.3.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df2d2afa5af41bfa09dc9d5a8e6d73ae39b677a8572200c65a5ea353387ffccd"}, - {file = "frozendict-2.3.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b10df7f5d8637b1af319434f99dc25ca6f5537e28b293e4c405ebfb4bf9581fa"}, - {file = "frozendict-2.3.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da22a3e873f365f97445c49afc1e6d5198ed6d172f3efaf0e9fde0edcca3cea1"}, - {file = "frozendict-2.3.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89218738e2122b50bf8a0444083dbe2de280402e9c2ef0929c0db0f93ff11271"}, - {file = "frozendict-2.3.10-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:aa11add43a71fd47523fbd011be5cc011df79e25ec0b0339fc0d728623aaa7ec"}, - {file = "frozendict-2.3.10-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:af267bd6d98cbc10580105dc76f28f7156856fa48a5bbcadd40edb85f93657ae"}, - {file = "frozendict-2.3.10-cp310-cp310-win_amd64.whl", hash = "sha256:c112024df64b8926a315d7e36b860967fcad8aae0c592b9f117589391373e893"}, - {file = "frozendict-2.3.10-cp310-cp310-win_arm64.whl", hash = "sha256:a0065db2bc76628853dd620bd08c1ca44ad0b711e92e89b4156493153add6f9d"}, - {file = "frozendict-2.3.10-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:93634af5a6d71762aebc7d78bdce92890b7e612588faf887c9eaf752dc7ccdb1"}, - {file = "frozendict-2.3.10-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b4d05e231dc1a2ec874f847fd7348cbee469555468efb875a89994ecde31a81"}, - {file = "frozendict-2.3.10-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d40d0644f19365fc6cc428db31c0f113fa550bd15920262f9d77ccf6556d87b"}, - {file = "frozendict-2.3.10-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:12b40526219f9583b30690011288bca4d6cce8724cda96b3c3ab08b67c5a7f09"}, - {file = "frozendict-2.3.10-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:6b552fffeba8e41b43ce10cc0fc467e048a7c9a71ae3241057510342132555b9"}, - {file = "frozendict-2.3.10-cp36-cp36m-win_amd64.whl", hash = "sha256:07208e4718cb70aa259ac886c19b96a4aad1cf00e9199f211746f738951bbf7c"}, - {file = "frozendict-2.3.10-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e8bec6d11f7254e405290cb1b081caffa0c18b6aa779130da9a546349c56be83"}, - {file = "frozendict-2.3.10-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b089c7e8c95d8b043e82e7da26e165f4220d7310efaad5e94445db7e3bc8321e"}, - {file = "frozendict-2.3.10-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08a5829d708657c9d5ad58f4a7e4baa73a3d57290f9613bdd909d481fc203a3a"}, - {file = "frozendict-2.3.10-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1c015852dacf144dbeadf203673d8c714f788fcc2b810a36504994b3c4f5a436"}, - {file = "frozendict-2.3.10-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bb9f15a5ed924be2b1cb3654b7ea3b7bae265ff39e2b5784d42bd4a6e1353e45"}, - {file = "frozendict-2.3.10-cp37-cp37m-win_amd64.whl", hash = "sha256:809bb9c6c657bded925710a309bb2a2350bdbfdc9371df427f1a93cb8ab7ec3e"}, - {file = "frozendict-2.3.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ff7a9cca3a3a1e584349e859d028388bd96a5475f76721471b73797472c6db17"}, - {file = "frozendict-2.3.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0cdd496933ddb428f3854bea9ffdce0245bb27c27909f663ad396409fb4dffb5"}, - {file = "frozendict-2.3.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9df392b655fadaa0174c1923e6205b30ad1ccca248e8e146e63a8147a355ee01"}, - {file = "frozendict-2.3.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7901828700f36fe12486705afe7afc5583434390c8f69b5419de1b6c566fb00d"}, - {file = "frozendict-2.3.10-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c9aa28ce48d848ee520409533fd0254de4caf025c5cf1b9f27c98c1dd8cf90aa"}, - {file = "frozendict-2.3.10-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0856af4f5b4288b2270e0b74078fad5cbaf4f799326b82183865f6f367008b2c"}, - {file = "frozendict-2.3.10-cp38-cp38-win_amd64.whl", hash = "sha256:ac41c671ff33cbefc0f06c4b2a630d18ab59f5256f45f57d5632252ae4a8c07a"}, - {file = "frozendict-2.3.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:893205dc5a4e5c4b24e5822ceb21ef14fed8ca4afae7ac688e2fc24294c85225"}, - {file = "frozendict-2.3.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e78c5ac5d71f3b73f07ff9d9e3cc32dfbf7954f2c57c2d0e1fe8f1600e980b40"}, - {file = "frozendict-2.3.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c4ca4cc42bc30b20476616411d4b49aae6084760b99251f1cbdfed879ae53ea"}, - {file = "frozendict-2.3.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c865962216f7cfd6dac8693f4de431a9d98a7225185ff23613ecd10c42423adc"}, - {file = "frozendict-2.3.10-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:99b2f47b292cc4d68f6679918e8e9e6dc5e816924d8369d07018be56b93fb20f"}, - {file = "frozendict-2.3.10-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e7abf4539b73c8e5680dd2fdbd19ca4fc3e2b2f3666f80f022217839bb859fd"}, - {file = "frozendict-2.3.10-cp39-cp39-win_amd64.whl", hash = "sha256:901e774629fc63f84d24b5e46b59de1eed22392ee98b7f92e694a127d541edac"}, - {file = "frozendict-2.3.10-cp39-cp39-win_arm64.whl", hash = "sha256:6f8681c0ffe92be9aba40c9b9960c48f0ae7f6ea585af2b93fc9542cc3865969"}, - {file = "frozendict-2.3.10-py3-none-any.whl", hash = "sha256:66cded65f144393b4226bda9fe9ac2f42451d2d603e8a486015744bb566a7008"}, - {file = "frozendict-2.3.10.tar.gz", hash = "sha256:aadc83510ce82751a0bb3575231f778bc37cbb373f5f05a52b888e26cbb92f79"}, + {file = "frozendict-2.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:475c65202a6f5421df8cacb8a2f29c5087134a0542b0540ae95fbf4db7af2ff9"}, + {file = "frozendict-2.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2607e82efdd2c277224a58bda3994d4cd48e49eff7fa31e404cf3066e8dbfeae"}, + {file = "frozendict-2.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fd4583194baabe100c135883017da76259a315d34e303eddf198541b7e02e44"}, + {file = "frozendict-2.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efca7281184b54f7abab6980cf25837b709f72ced62791f62dabcd7b184d958a"}, + {file = "frozendict-2.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fc4cba1ced988ce9020dfcaae6fe3f5521eebc00c5772b511aaf691b0be91e6"}, + {file = "frozendict-2.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8fab616e7c0fea2ac928f107c740bd9ba516fc083adfcd1c391d6bfc9164403d"}, + {file = "frozendict-2.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:09ba8ee37d260adde311b8eb4cd12bf27f64071242f736757ae6a11d331eb860"}, + {file = "frozendict-2.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:0615ed71570eec3cc96df063930ea6e563211efeeac86e3f3cc8bdfc9c9bfab7"}, + {file = "frozendict-2.4.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc754117a7d60ba8e55b3c39abd67f37fbc05dd63cdcb03d1717a382fe0a3421"}, + {file = "frozendict-2.4.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2804ea4bd2179bb33b99483cc8d69246630cc00632b9affe2914e8666f1cc7e5"}, + {file = "frozendict-2.4.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd4700c3f0aebdc8f4375c35590135794b1dbf2aca132f4756b584fa9910af2d"}, + {file = "frozendict-2.4.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:da4406d95c340e0b1cc43a3858fac729f52689325bcf61a9182eb94aff7451dc"}, + {file = "frozendict-2.4.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:1875e7b70a5724bf964354da8fd542240d2cead0d80053ac96bf4494ce3517fa"}, + {file = "frozendict-2.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a60f353496637ca21396289a7d969af1eb4ec4d11a7c37a0e7f25fc1761a0c97"}, + {file = "frozendict-2.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b666f9c6c8a9e794d2713a944b10a65480ff459579d75b5f686c75031c2c2dfc"}, + {file = "frozendict-2.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9d81fb396ea81fcba3b3dde4a4b51adcb74ff31632014fbfd030f8acd5a7292"}, + {file = "frozendict-2.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4925c8e82d2bd23d45996cd0827668a52b9c51103897c98ce409a763d0c00c61"}, + {file = "frozendict-2.4.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:aa86325da6a6071284b4ed3d9d2cd9db068560aebad503b658d6a889a0575683"}, + {file = "frozendict-2.4.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5bb5b62d4e2bce12e91800496d94de41bec8f16e4d8a7b16e8f263676ae2031a"}, + {file = "frozendict-2.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3909df909516cfd7bcefd9a3003948970a12a50c5648d8bbddafcef171f2117f"}, + {file = "frozendict-2.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:204f2c5c10fc018d1ba8ccc67758aa83fe769c782547bd26dc250317a7ccba71"}, + {file = "frozendict-2.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d8d1d269874c94b1ed2b6667e5e43dcf4541838019b1caa4c48f848ac73634df"}, + {file = "frozendict-2.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:809f1cffb602cf06e5186c69c0e3b74bec7a3684593145331f9aa2a65b5ba3b7"}, + {file = "frozendict-2.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b017cba5f73869b04c2977139ad08e57a7480de1e384c34193939698119baa1d"}, + {file = "frozendict-2.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0b75e5e231621dedaef88334997e79fbd137dd89895543d3862fe0220fc3572c"}, + {file = "frozendict-2.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:df3819a5d48ab3aae1548e62093d0111ad7c3b62ff9392421b7bbf149c08b629"}, + {file = "frozendict-2.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:42a9b33ccf9d417b22146e59803c53d5c39d7d9151d2df8df59c235f6a1a5ed7"}, + {file = "frozendict-2.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a3f51bfa64e0c4a6608e3f2878bab1211a6b3b197de6fa57151bbe73f1184457"}, + {file = "frozendict-2.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a1d232f092dc686e6ef23d436bde30f82c018f31cef1b89b31caef03814b1617"}, + {file = "frozendict-2.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e530658134e88607ff8c2c8934a07b2bb5e9fffab5045f127746f6542c6c77e"}, + {file = "frozendict-2.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23a52bbea30c9e35b89291273944393770fb031e522a172e3aff19b62cc50047"}, + {file = "frozendict-2.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f91acaff475d0ef0d3436b805c9b91fc627a6a8a281771a24f7ab7f458a0b34f"}, + {file = "frozendict-2.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:08d9c7c1aa92b94538b3a79c43999f999012e174588435f197794d5e5a80e0f5"}, + {file = "frozendict-2.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:05c5a77957ecba4286c7ab33861a8f4f2badc7ea86fc82b834fb360d3aa4c108"}, + {file = "frozendict-2.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:c8af8a6a39e0050d3f3193cda56c42b43534a9b3995c44241bb9527e3c3fd451"}, + {file = "frozendict-2.4.0.tar.gz", hash = "sha256:c26758198e403337933a92b01f417a8240c954f553e1d4b5e0f8e39d9c8e3f0a"}, ] [[package]] @@ -1172,20 +1175,20 @@ smmap = ">=3.0.1,<6" [[package]] name = "gitpython" -version = "3.1.40" +version = "3.1.41" description = "GitPython is a Python library used to interact with Git repositories" optional = false python-versions = ">=3.7" files = [ - {file = "GitPython-3.1.40-py3-none-any.whl", hash = "sha256:cf14627d5a8049ffbf49915732e5eddbe8134c3bdb9d476e6182b676fc573f8a"}, - {file = "GitPython-3.1.40.tar.gz", hash = "sha256:22b126e9ffb671fdd0c129796343a02bf67bf2994b35449ffc9321aa755e18a4"}, + {file = "GitPython-3.1.41-py3-none-any.whl", hash = "sha256:c36b6634d069b3f719610175020a9aed919421c87552185b085e04fbbdb10b7c"}, + {file = "GitPython-3.1.41.tar.gz", hash = "sha256:ed66e624884f76df22c8e16066d567aaa5a37d5b5fa19db2c6df6f7156db9048"}, ] [package.dependencies] gitdb = ">=4.0.1,<5" [package.extras] -test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest", "pytest-cov", "pytest-instafail", "pytest-subtests", "pytest-sugar"] +test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "sumtypes"] [[package]] name = "grandalf" @@ -1275,13 +1278,13 @@ tests = ["freezegun", "pytest", "pytest-cov"] [[package]] name = "identify" -version = "2.5.32" +version = "2.5.33" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.5.32-py2.py3-none-any.whl", hash = "sha256:0b7656ef6cba81664b783352c73f8c24b39cf82f926f78f4550eda928e5e0545"}, - {file = "identify-2.5.32.tar.gz", hash = "sha256:5d9979348ec1a21c768ae07e0a652924538e8bce67313a73cb0f681cf08ba407"}, + {file = "identify-2.5.33-py2.py3-none-any.whl", hash = "sha256:d40ce5fcd762817627670da8a7d8d8e65f24342d14539c59488dc603bf662e34"}, + {file = "identify-2.5.33.tar.gz", hash = "sha256:161558f9fe4559e1557e1bff323e8631f6a0e4837f7497767c1782832f16b62d"}, ] [package.extras] @@ -1311,20 +1314,20 @@ files = [ [[package]] name = "importlib-metadata" -version = "6.8.0" +version = "7.0.1" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-6.8.0-py3-none-any.whl", hash = "sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb"}, - {file = "importlib_metadata-6.8.0.tar.gz", hash = "sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743"}, + {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"}, + {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] @@ -1383,20 +1386,17 @@ six = "*" [[package]] name = "isort" -version = "5.12.0" +version = "5.13.2" description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.8.0" files = [ - {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, - {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, ] [package.extras] -colors = ["colorama (>=0.4.3)"] -pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] -plugins = ["setuptools"] -requirements-deprecated-finder = ["pip-api", "pipreqs"] +colors = ["colorama (>=0.4.6)"] [[package]] name = "itsdangerous" @@ -1411,13 +1411,13 @@ files = [ [[package]] name = "jinja2" -version = "3.1.2" +version = "3.1.3" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, ] [package.dependencies] @@ -1428,13 +1428,13 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "jinxed" -version = "1.2.0" +version = "1.2.1" description = "Jinxed Terminal Library" optional = false python-versions = "*" files = [ - {file = "jinxed-1.2.0-py2.py3-none-any.whl", hash = "sha256:cfc2b2e4e3b4326954d546ba6d6b9a7a796ddcb0aef8d03161d005177eb0d48b"}, - {file = "jinxed-1.2.0.tar.gz", hash = "sha256:032acda92d5c57cd216033cbbd53de731e6ed50deb63eb4781336ca55f72cda5"}, + {file = "jinxed-1.2.1-py2.py3-none-any.whl", hash = "sha256:37422659c4925969c66148c5e64979f553386a4226b9484d910d3094ced37d30"}, + {file = "jinxed-1.2.1.tar.gz", hash = "sha256:30c3f861b73279fea1ed928cfd4dfb1f273e16cd62c8a32acfac362da0f78f3f"}, ] [package.dependencies] @@ -1442,47 +1442,48 @@ ansicon = {version = "*", markers = "platform_system == \"Windows\""} [[package]] name = "lazy-object-proxy" -version = "1.9.0" +version = "1.10.0" description = "A fast and thorough lazy object proxy." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "lazy-object-proxy-1.9.0.tar.gz", hash = "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-win32.whl", hash = "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-win32.whl", hash = "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win32.whl", hash = "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-win32.whl", hash = "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-win32.whl", hash = "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f"}, + {file = "lazy-object-proxy-1.10.0.tar.gz", hash = "sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:855e068b0358ab916454464a884779c7ffa312b8925c6f7401e952dcf3b89977"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab7004cf2e59f7c2e4345604a3e6ea0d92ac44e1c2375527d56492014e690c3"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc0d2fc424e54c70c4bc06787e4072c4f3b1aa2f897dfdc34ce1013cf3ceef05"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e2adb09778797da09d2b5ebdbceebf7dd32e2c96f79da9052b2e87b6ea495895"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1f711e2c6dcd4edd372cf5dec5c5a30d23bba06ee012093267b3376c079ec83"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-win32.whl", hash = "sha256:76a095cfe6045c7d0ca77db9934e8f7b71b14645f0094ffcd842349ada5c5fb9"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:b4f87d4ed9064b2628da63830986c3d2dca7501e6018347798313fcf028e2fd4"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fec03caabbc6b59ea4a638bee5fce7117be8e99a4103d9d5ad77f15d6f81020c"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02c83f957782cbbe8136bee26416686a6ae998c7b6191711a04da776dc9e47d4"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009e6bb1f1935a62889ddc8541514b6a9e1fcf302667dcb049a0be5c8f613e56"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75fc59fc450050b1b3c203c35020bc41bd2695ed692a392924c6ce180c6f1dc9"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:782e2c9b2aab1708ffb07d4bf377d12901d7a1d99e5e410d648d892f8967ab1f"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-win32.whl", hash = "sha256:edb45bb8278574710e68a6b021599a10ce730d156e5b254941754a9cc0b17d03"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:e271058822765ad5e3bca7f05f2ace0de58a3f4e62045a8c90a0dfd2f8ad8cc6"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e98c8af98d5707dcdecc9ab0863c0ea6e88545d42ca7c3feffb6b4d1e370c7ba"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:952c81d415b9b80ea261d2372d2a4a2332a3890c2b83e0535f263ddfe43f0d43"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80b39d3a151309efc8cc48675918891b865bdf742a8616a337cb0090791a0de9"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e221060b701e2aa2ea991542900dd13907a5c90fa80e199dbf5a03359019e7a3"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92f09ff65ecff3108e56526f9e2481b8116c0b9e1425325e13245abfd79bdb1b"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-win32.whl", hash = "sha256:3ad54b9ddbe20ae9f7c1b29e52f123120772b06dbb18ec6be9101369d63a4074"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:127a789c75151db6af398b8972178afe6bda7d6f68730c057fbbc2e96b08d282"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e4ed0518a14dd26092614412936920ad081a424bdcb54cc13349a8e2c6d106a"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ad9e6ed739285919aa9661a5bbed0aaf410aa60231373c5579c6b4801bd883c"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fc0a92c02fa1ca1e84fc60fa258458e5bf89d90a1ddaeb8ed9cc3147f417255"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0aefc7591920bbd360d57ea03c995cebc204b424524a5bd78406f6e1b8b2a5d8"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5faf03a7d8942bb4476e3b62fd0f4cf94eaf4618e304a19865abf89a35c0bbee"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-win32.whl", hash = "sha256:e333e2324307a7b5d86adfa835bb500ee70bfcd1447384a822e96495796b0ca4"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:cb73507defd385b7705c599a94474b1d5222a508e502553ef94114a143ec6696"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:366c32fe5355ef5fc8a232c5436f4cc66e9d3e8967c01fb2e6302fd6627e3d94"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2297f08f08a2bb0d32a4265e98a006643cd7233fb7983032bd61ac7a02956b3b"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18dd842b49456aaa9a7cf535b04ca4571a302ff72ed8740d06b5adcd41fe0757"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:217138197c170a2a74ca0e05bddcd5f1796c735c37d0eee33e43259b192aa424"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9a3a87cf1e133e5b1994144c12ca4aa3d9698517fe1e2ca82977781b16955658"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-win32.whl", hash = "sha256:30b339b2a743c5288405aa79a69e706a06e02958eab31859f7f3c04980853b70"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:a899b10e17743683b293a729d3a11f2f399e8a90c73b089e29f5d0fe3509f0dd"}, + {file = "lazy_object_proxy-1.10.0-pp310.pp311.pp312.pp38.pp39-none-any.whl", hash = "sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d"}, ] [[package]] @@ -1588,110 +1589,96 @@ files = [ [[package]] name = "lxml" -version = "4.9.3" +version = "5.1.0" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" -files = [ - {file = "lxml-4.9.3-cp27-cp27m-macosx_11_0_x86_64.whl", hash = "sha256:b0a545b46b526d418eb91754565ba5b63b1c0b12f9bd2f808c852d9b4b2f9b5c"}, - {file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:075b731ddd9e7f68ad24c635374211376aa05a281673ede86cbe1d1b3455279d"}, - {file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1e224d5755dba2f4a9498e150c43792392ac9b5380aa1b845f98a1618c94eeef"}, - {file = "lxml-4.9.3-cp27-cp27m-win32.whl", hash = "sha256:2c74524e179f2ad6d2a4f7caf70e2d96639c0954c943ad601a9e146c76408ed7"}, - {file = "lxml-4.9.3-cp27-cp27m-win_amd64.whl", hash = "sha256:4f1026bc732b6a7f96369f7bfe1a4f2290fb34dce00d8644bc3036fb351a4ca1"}, - {file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c0781a98ff5e6586926293e59480b64ddd46282953203c76ae15dbbbf302e8bb"}, - {file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cef2502e7e8a96fe5ad686d60b49e1ab03e438bd9123987994528febd569868e"}, - {file = "lxml-4.9.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b86164d2cff4d3aaa1f04a14685cbc072efd0b4f99ca5708b2ad1b9b5988a991"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:42871176e7896d5d45138f6d28751053c711ed4d48d8e30b498da155af39aebd"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ae8b9c6deb1e634ba4f1930eb67ef6e6bf6a44b6eb5ad605642b2d6d5ed9ce3c"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:411007c0d88188d9f621b11d252cce90c4a2d1a49db6c068e3c16422f306eab8"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:cd47b4a0d41d2afa3e58e5bf1f62069255aa2fd6ff5ee41604418ca925911d76"}, - {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e2cb47860da1f7e9a5256254b74ae331687b9672dfa780eed355c4c9c3dbd23"}, - {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1247694b26342a7bf47c02e513d32225ededd18045264d40758abeb3c838a51f"}, - {file = "lxml-4.9.3-cp310-cp310-win32.whl", hash = "sha256:cdb650fc86227eba20de1a29d4b2c1bfe139dc75a0669270033cb2ea3d391b85"}, - {file = "lxml-4.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:97047f0d25cd4bcae81f9ec9dc290ca3e15927c192df17331b53bebe0e3ff96d"}, - {file = "lxml-4.9.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:1f447ea5429b54f9582d4b955f5f1985f278ce5cf169f72eea8afd9502973dd5"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:57d6ba0ca2b0c462f339640d22882acc711de224d769edf29962b09f77129cbf"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:9767e79108424fb6c3edf8f81e6730666a50feb01a328f4a016464a5893f835a"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:71c52db65e4b56b8ddc5bb89fb2e66c558ed9d1a74a45ceb7dcb20c191c3df2f"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d73d8ecf8ecf10a3bd007f2192725a34bd62898e8da27eb9d32a58084f93962b"}, - {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a3d3487f07c1d7f150894c238299934a2a074ef590b583103a45002035be120"}, - {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e28c51fa0ce5674be9f560c6761c1b441631901993f76700b1b30ca6c8378d6"}, - {file = "lxml-4.9.3-cp311-cp311-win32.whl", hash = "sha256:0bfd0767c5c1de2551a120673b72e5d4b628737cb05414f03c3277bf9bed3305"}, - {file = "lxml-4.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:25f32acefac14ef7bd53e4218fe93b804ef6f6b92ffdb4322bb6d49d94cad2bc"}, - {file = "lxml-4.9.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:d3ff32724f98fbbbfa9f49d82852b159e9784d6094983d9a8b7f2ddaebb063d4"}, - {file = "lxml-4.9.3-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:48d6ed886b343d11493129e019da91d4039826794a3e3027321c56d9e71505be"}, - {file = "lxml-4.9.3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9a92d3faef50658dd2c5470af249985782bf754c4e18e15afb67d3ab06233f13"}, - {file = "lxml-4.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b4e4bc18382088514ebde9328da057775055940a1f2e18f6ad2d78aa0f3ec5b9"}, - {file = "lxml-4.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fc9b106a1bf918db68619fdcd6d5ad4f972fdd19c01d19bdb6bf63f3589a9ec5"}, - {file = "lxml-4.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:d37017287a7adb6ab77e1c5bee9bcf9660f90ff445042b790402a654d2ad81d8"}, - {file = "lxml-4.9.3-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:56dc1f1ebccc656d1b3ed288f11e27172a01503fc016bcabdcbc0978b19352b7"}, - {file = "lxml-4.9.3-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:578695735c5a3f51569810dfebd05dd6f888147a34f0f98d4bb27e92b76e05c2"}, - {file = "lxml-4.9.3-cp35-cp35m-win32.whl", hash = "sha256:704f61ba8c1283c71b16135caf697557f5ecf3e74d9e453233e4771d68a1f42d"}, - {file = "lxml-4.9.3-cp35-cp35m-win_amd64.whl", hash = "sha256:c41bfca0bd3532d53d16fd34d20806d5c2b1ace22a2f2e4c0008570bf2c58833"}, - {file = "lxml-4.9.3-cp36-cp36m-macosx_11_0_x86_64.whl", hash = "sha256:64f479d719dc9f4c813ad9bb6b28f8390360660b73b2e4beb4cb0ae7104f1c12"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:dd708cf4ee4408cf46a48b108fb9427bfa00b9b85812a9262b5c668af2533ea5"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c31c7462abdf8f2ac0577d9f05279727e698f97ecbb02f17939ea99ae8daa98"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e3cd95e10c2610c360154afdc2f1480aea394f4a4f1ea0a5eacce49640c9b190"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:4930be26af26ac545c3dffb662521d4e6268352866956672231887d18f0eaab2"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4aec80cde9197340bc353d2768e2a75f5f60bacda2bab72ab1dc499589b3878c"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:14e019fd83b831b2e61baed40cab76222139926b1fb5ed0e79225bc0cae14584"}, - {file = "lxml-4.9.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0c0850c8b02c298d3c7006b23e98249515ac57430e16a166873fc47a5d549287"}, - {file = "lxml-4.9.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:aca086dc5f9ef98c512bac8efea4483eb84abbf926eaeedf7b91479feb092458"}, - {file = "lxml-4.9.3-cp36-cp36m-win32.whl", hash = "sha256:50baa9c1c47efcaef189f31e3d00d697c6d4afda5c3cde0302d063492ff9b477"}, - {file = "lxml-4.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bef4e656f7d98aaa3486d2627e7d2df1157d7e88e7efd43a65aa5dd4714916cf"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:46f409a2d60f634fe550f7133ed30ad5321ae2e6630f13657fb9479506b00601"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4c28a9144688aef80d6ea666c809b4b0e50010a2aca784c97f5e6bf143d9f129"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:141f1d1a9b663c679dc524af3ea1773e618907e96075262726c7612c02b149a4"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:53ace1c1fd5a74ef662f844a0413446c0629d151055340e9893da958a374f70d"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17a753023436a18e27dd7769e798ce302963c236bc4114ceee5b25c18c52c693"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7d298a1bd60c067ea75d9f684f5f3992c9d6766fadbc0bcedd39750bf344c2f4"}, - {file = "lxml-4.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:081d32421db5df44c41b7f08a334a090a545c54ba977e47fd7cc2deece78809a"}, - {file = "lxml-4.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:23eed6d7b1a3336ad92d8e39d4bfe09073c31bfe502f20ca5116b2a334f8ec02"}, - {file = "lxml-4.9.3-cp37-cp37m-win32.whl", hash = "sha256:1509dd12b773c02acd154582088820893109f6ca27ef7291b003d0e81666109f"}, - {file = "lxml-4.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:120fa9349a24c7043854c53cae8cec227e1f79195a7493e09e0c12e29f918e52"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4d2d1edbca80b510443f51afd8496be95529db04a509bc8faee49c7b0fb6d2cc"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8d7e43bd40f65f7d97ad8ef5c9b1778943d02f04febef12def25f7583d19baac"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:71d66ee82e7417828af6ecd7db817913cb0cf9d4e61aa0ac1fde0583d84358db"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:6fc3c450eaa0b56f815c7b62f2b7fba7266c4779adcf1cece9e6deb1de7305ce"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65299ea57d82fb91c7f019300d24050c4ddeb7c5a190e076b5f48a2b43d19c42"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:eadfbbbfb41b44034a4c757fd5d70baccd43296fb894dba0295606a7cf3124aa"}, - {file = "lxml-4.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3e9bdd30efde2b9ccfa9cb5768ba04fe71b018a25ea093379c857c9dad262c40"}, - {file = "lxml-4.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fcdd00edfd0a3001e0181eab3e63bd5c74ad3e67152c84f93f13769a40e073a7"}, - {file = "lxml-4.9.3-cp38-cp38-win32.whl", hash = "sha256:57aba1bbdf450b726d58b2aea5fe47c7875f5afb2c4a23784ed78f19a0462574"}, - {file = "lxml-4.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:92af161ecbdb2883c4593d5ed4815ea71b31fafd7fd05789b23100d081ecac96"}, - {file = "lxml-4.9.3-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:9bb6ad405121241e99a86efff22d3ef469024ce22875a7ae045896ad23ba2340"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8ed74706b26ad100433da4b9d807eae371efaa266ffc3e9191ea436087a9d6a7"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fbf521479bcac1e25a663df882c46a641a9bff6b56dc8b0fafaebd2f66fb231b"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:303bf1edce6ced16bf67a18a1cf8339d0db79577eec5d9a6d4a80f0fb10aa2da"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:5515edd2a6d1a5a70bfcdee23b42ec33425e405c5b351478ab7dc9347228f96e"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:690dafd0b187ed38583a648076865d8c229661ed20e48f2335d68e2cf7dc829d"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b6420a005548ad52154c8ceab4a1290ff78d757f9e5cbc68f8c77089acd3c432"}, - {file = "lxml-4.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bb3bb49c7a6ad9d981d734ef7c7193bc349ac338776a0360cc671eaee89bcf69"}, - {file = "lxml-4.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d27be7405547d1f958b60837dc4c1007da90b8b23f54ba1f8b728c78fdb19d50"}, - {file = "lxml-4.9.3-cp39-cp39-win32.whl", hash = "sha256:8df133a2ea5e74eef5e8fc6f19b9e085f758768a16e9877a60aec455ed2609b2"}, - {file = "lxml-4.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:4dd9a263e845a72eacb60d12401e37c616438ea2e5442885f65082c276dfb2b2"}, - {file = "lxml-4.9.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6689a3d7fd13dc687e9102a27e98ef33730ac4fe37795d5036d18b4d527abd35"}, - {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f6bdac493b949141b733c5345b6ba8f87a226029cbabc7e9e121a413e49441e0"}, - {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:05186a0f1346ae12553d66df1cfce6f251589fea3ad3da4f3ef4e34b2d58c6a3"}, - {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c2006f5c8d28dee289f7020f721354362fa304acbaaf9745751ac4006650254b"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:5c245b783db29c4e4fbbbfc9c5a78be496c9fea25517f90606aa1f6b2b3d5f7b"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4fb960a632a49f2f089d522f70496640fdf1218f1243889da3822e0a9f5f3ba7"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:50670615eaf97227d5dc60de2dc99fb134a7130d310d783314e7724bf163f75d"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9719fe17307a9e814580af1f5c6e05ca593b12fb7e44fe62450a5384dbf61b4b"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3331bece23c9ee066e0fb3f96c61322b9e0f54d775fccefff4c38ca488de283a"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:ed667f49b11360951e201453fc3967344d0d0263aa415e1619e85ae7fd17b4e0"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8b77946fd508cbf0fccd8e400a7f71d4ac0e1595812e66025bac475a8e811694"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e4da8ca0c0c0aea88fd46be8e44bd49716772358d648cce45fe387f7b92374a7"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fe4bda6bd4340caa6e5cf95e73f8fea5c4bfc55763dd42f1b50a94c1b4a2fbd4"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f3df3db1d336b9356dd3112eae5f5c2b8b377f3bc826848567f10bfddfee77e9"}, - {file = "lxml-4.9.3.tar.gz", hash = "sha256:48628bd53a426c9eb9bc066a923acaa0878d1e86129fd5359aee99285f4eed9c"}, +python-versions = ">=3.6" +files = [ + {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:704f5572ff473a5f897745abebc6df40f22d4133c1e0a1f124e4f2bd3330ff7e"}, + {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9d3c0f8567ffe7502d969c2c1b809892dc793b5d0665f602aad19895f8d508da"}, + {file = "lxml-5.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5fcfbebdb0c5d8d18b84118842f31965d59ee3e66996ac842e21f957eb76138c"}, + {file = "lxml-5.1.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f37c6d7106a9d6f0708d4e164b707037b7380fcd0b04c5bd9cae1fb46a856fb"}, + {file = "lxml-5.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2befa20a13f1a75c751f47e00929fb3433d67eb9923c2c0b364de449121f447c"}, + {file = "lxml-5.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22b7ee4c35f374e2c20337a95502057964d7e35b996b1c667b5c65c567d2252a"}, + {file = "lxml-5.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bf8443781533b8d37b295016a4b53c1494fa9a03573c09ca5104550c138d5c05"}, + {file = "lxml-5.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:82bddf0e72cb2af3cbba7cec1d2fd11fda0de6be8f4492223d4a268713ef2147"}, + {file = "lxml-5.1.0-cp310-cp310-win32.whl", hash = "sha256:b66aa6357b265670bb574f050ffceefb98549c721cf28351b748be1ef9577d93"}, + {file = "lxml-5.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:4946e7f59b7b6a9e27bef34422f645e9a368cb2be11bf1ef3cafc39a1f6ba68d"}, + {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:14deca1460b4b0f6b01f1ddc9557704e8b365f55c63070463f6c18619ebf964f"}, + {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ed8c3d2cd329bf779b7ed38db176738f3f8be637bb395ce9629fc76f78afe3d4"}, + {file = "lxml-5.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:436a943c2900bb98123b06437cdd30580a61340fbdb7b28aaf345a459c19046a"}, + {file = "lxml-5.1.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acb6b2f96f60f70e7f34efe0c3ea34ca63f19ca63ce90019c6cbca6b676e81fa"}, + {file = "lxml-5.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af8920ce4a55ff41167ddbc20077f5698c2e710ad3353d32a07d3264f3a2021e"}, + {file = "lxml-5.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cfced4a069003d8913408e10ca8ed092c49a7f6cefee9bb74b6b3e860683b45"}, + {file = "lxml-5.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9e5ac3437746189a9b4121db2a7b86056ac8786b12e88838696899328fc44bb2"}, + {file = "lxml-5.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4c9bda132ad108b387c33fabfea47866af87f4ea6ffb79418004f0521e63204"}, + {file = "lxml-5.1.0-cp311-cp311-win32.whl", hash = "sha256:bc64d1b1dab08f679fb89c368f4c05693f58a9faf744c4d390d7ed1d8223869b"}, + {file = "lxml-5.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5ab722ae5a873d8dcee1f5f45ddd93c34210aed44ff2dc643b5025981908cda"}, + {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9aa543980ab1fbf1720969af1d99095a548ea42e00361e727c58a40832439114"}, + {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6f11b77ec0979f7e4dc5ae081325a2946f1fe424148d3945f943ceaede98adb8"}, + {file = "lxml-5.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a36c506e5f8aeb40680491d39ed94670487ce6614b9d27cabe45d94cd5d63e1e"}, + {file = "lxml-5.1.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f643ffd2669ffd4b5a3e9b41c909b72b2a1d5e4915da90a77e119b8d48ce867a"}, + {file = "lxml-5.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16dd953fb719f0ffc5bc067428fc9e88f599e15723a85618c45847c96f11f431"}, + {file = "lxml-5.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16018f7099245157564d7148165132c70adb272fb5a17c048ba70d9cc542a1a1"}, + {file = "lxml-5.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:82cd34f1081ae4ea2ede3d52f71b7be313756e99b4b5f829f89b12da552d3aa3"}, + {file = "lxml-5.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:19a1bc898ae9f06bccb7c3e1dfd73897ecbbd2c96afe9095a6026016e5ca97b8"}, + {file = "lxml-5.1.0-cp312-cp312-win32.whl", hash = "sha256:13521a321a25c641b9ea127ef478b580b5ec82aa2e9fc076c86169d161798b01"}, + {file = "lxml-5.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:1ad17c20e3666c035db502c78b86e58ff6b5991906e55bdbef94977700c72623"}, + {file = "lxml-5.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:24ef5a4631c0b6cceaf2dbca21687e29725b7c4e171f33a8f8ce23c12558ded1"}, + {file = "lxml-5.1.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d2900b7f5318bc7ad8631d3d40190b95ef2aa8cc59473b73b294e4a55e9f30f"}, + {file = "lxml-5.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:601f4a75797d7a770daed8b42b97cd1bb1ba18bd51a9382077a6a247a12aa38d"}, + {file = "lxml-5.1.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4b68c961b5cc402cbd99cca5eb2547e46ce77260eb705f4d117fd9c3f932b95"}, + {file = "lxml-5.1.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:afd825e30f8d1f521713a5669b63657bcfe5980a916c95855060048b88e1adb7"}, + {file = "lxml-5.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:262bc5f512a66b527d026518507e78c2f9c2bd9eb5c8aeeb9f0eb43fcb69dc67"}, + {file = "lxml-5.1.0-cp36-cp36m-win32.whl", hash = "sha256:e856c1c7255c739434489ec9c8aa9cdf5179785d10ff20add308b5d673bed5cd"}, + {file = "lxml-5.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:c7257171bb8d4432fe9d6fdde4d55fdbe663a63636a17f7f9aaba9bcb3153ad7"}, + {file = "lxml-5.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b9e240ae0ba96477682aa87899d94ddec1cc7926f9df29b1dd57b39e797d5ab5"}, + {file = "lxml-5.1.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a96f02ba1bcd330807fc060ed91d1f7a20853da6dd449e5da4b09bfcc08fdcf5"}, + {file = "lxml-5.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e3898ae2b58eeafedfe99e542a17859017d72d7f6a63de0f04f99c2cb125936"}, + {file = "lxml-5.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61c5a7edbd7c695e54fca029ceb351fc45cd8860119a0f83e48be44e1c464862"}, + {file = "lxml-5.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3aeca824b38ca78d9ee2ab82bd9883083d0492d9d17df065ba3b94e88e4d7ee6"}, + {file = "lxml-5.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8f52fe6859b9db71ee609b0c0a70fea5f1e71c3462ecf144ca800d3f434f0764"}, + {file = "lxml-5.1.0-cp37-cp37m-win32.whl", hash = "sha256:d42e3a3fc18acc88b838efded0e6ec3edf3e328a58c68fbd36a7263a874906c8"}, + {file = "lxml-5.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:eac68f96539b32fce2c9b47eb7c25bb2582bdaf1bbb360d25f564ee9e04c542b"}, + {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ae15347a88cf8af0949a9872b57a320d2605ae069bcdf047677318bc0bba45b1"}, + {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c26aab6ea9c54d3bed716b8851c8bfc40cb249b8e9880e250d1eddde9f709bf5"}, + {file = "lxml-5.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:342e95bddec3a698ac24378d61996b3ee5ba9acfeb253986002ac53c9a5f6f84"}, + {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:725e171e0b99a66ec8605ac77fa12239dbe061482ac854d25720e2294652eeaa"}, + {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d184e0d5c918cff04cdde9dbdf9600e960161d773666958c9d7b565ccc60c45"}, + {file = "lxml-5.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:98f3f020a2b736566c707c8e034945c02aa94e124c24f77ca097c446f81b01f1"}, + {file = "lxml-5.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d48fc57e7c1e3df57be5ae8614bab6d4e7b60f65c5457915c26892c41afc59e"}, + {file = "lxml-5.1.0-cp38-cp38-win32.whl", hash = "sha256:7ec465e6549ed97e9f1e5ed51c657c9ede767bc1c11552f7f4d022c4df4a977a"}, + {file = "lxml-5.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:b21b4031b53d25b0858d4e124f2f9131ffc1530431c6d1321805c90da78388d1"}, + {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:52427a7eadc98f9e62cb1368a5079ae826f94f05755d2d567d93ee1bc3ceb354"}, + {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6a2a2c724d97c1eb8cf966b16ca2915566a4904b9aad2ed9a09c748ffe14f969"}, + {file = "lxml-5.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843b9c835580d52828d8f69ea4302537337a21e6b4f1ec711a52241ba4a824f3"}, + {file = "lxml-5.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b99f564659cfa704a2dd82d0684207b1aadf7d02d33e54845f9fc78e06b7581"}, + {file = "lxml-5.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f8b0c78e7aac24979ef09b7f50da871c2de2def043d468c4b41f512d831e912"}, + {file = "lxml-5.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9bcf86dfc8ff3e992fed847c077bd875d9e0ba2fa25d859c3a0f0f76f07f0c8d"}, + {file = "lxml-5.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:49a9b4af45e8b925e1cd6f3b15bbba2c81e7dba6dce170c677c9cda547411e14"}, + {file = "lxml-5.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:280f3edf15c2a967d923bcfb1f8f15337ad36f93525828b40a0f9d6c2ad24890"}, + {file = "lxml-5.1.0-cp39-cp39-win32.whl", hash = "sha256:ed7326563024b6e91fef6b6c7a1a2ff0a71b97793ac33dbbcf38f6005e51ff6e"}, + {file = "lxml-5.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:8d7b4beebb178e9183138f552238f7e6613162a42164233e2bda00cb3afac58f"}, + {file = "lxml-5.1.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9bd0ae7cc2b85320abd5e0abad5ccee5564ed5f0cc90245d2f9a8ef330a8deae"}, + {file = "lxml-5.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8c1d679df4361408b628f42b26a5d62bd3e9ba7f0c0e7969f925021554755aa"}, + {file = "lxml-5.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2ad3a8ce9e8a767131061a22cd28fdffa3cd2dc193f399ff7b81777f3520e372"}, + {file = "lxml-5.1.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:304128394c9c22b6569eba2a6d98392b56fbdfbad58f83ea702530be80d0f9df"}, + {file = "lxml-5.1.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d74fcaf87132ffc0447b3c685a9f862ffb5b43e70ea6beec2fb8057d5d2a1fea"}, + {file = "lxml-5.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:8cf5877f7ed384dabfdcc37922c3191bf27e55b498fecece9fd5c2c7aaa34c33"}, + {file = "lxml-5.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:877efb968c3d7eb2dad540b6cabf2f1d3c0fbf4b2d309a3c141f79c7e0061324"}, + {file = "lxml-5.1.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f14a4fb1c1c402a22e6a341a24c1341b4a3def81b41cd354386dcb795f83897"}, + {file = "lxml-5.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:25663d6e99659544ee8fe1b89b1a8c0aaa5e34b103fab124b17fa958c4a324a6"}, + {file = "lxml-5.1.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8b9f19df998761babaa7f09e6bc169294eefafd6149aaa272081cbddc7ba4ca3"}, + {file = "lxml-5.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e53d7e6a98b64fe54775d23a7c669763451340c3d44ad5e3a3b48a1efbdc96f"}, + {file = "lxml-5.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c3cd1fc1dc7c376c54440aeaaa0dcc803d2126732ff5c6b68ccd619f2e64be4f"}, + {file = "lxml-5.1.0.tar.gz", hash = "sha256:3eea6ed6e6c918e468e693c41ef07f3c3acc310b70ddd9cc72d9ef84bc9564ca"}, ] [package.extras] cssselect = ["cssselect (>=0.7)"] html5 = ["html5lib"] htmlsoup = ["BeautifulSoup4"] -source = ["Cython (>=0.29.35)"] +source = ["Cython (>=3.0.7)"] [[package]] name = "markdown-it-py" @@ -2008,38 +1995,38 @@ files = [ [[package]] name = "mypy" -version = "1.7.1" +version = "1.8.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:12cce78e329838d70a204293e7b29af9faa3ab14899aec397798a4b41be7f340"}, - {file = "mypy-1.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1484b8fa2c10adf4474f016e09d7a159602f3239075c7bf9f1627f5acf40ad49"}, - {file = "mypy-1.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31902408f4bf54108bbfb2e35369877c01c95adc6192958684473658c322c8a5"}, - {file = "mypy-1.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f2c2521a8e4d6d769e3234350ba7b65ff5d527137cdcde13ff4d99114b0c8e7d"}, - {file = "mypy-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:fcd2572dd4519e8a6642b733cd3a8cfc1ef94bafd0c1ceed9c94fe736cb65b6a"}, - {file = "mypy-1.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4b901927f16224d0d143b925ce9a4e6b3a758010673eeded9b748f250cf4e8f7"}, - {file = "mypy-1.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2f7f6985d05a4e3ce8255396df363046c28bea790e40617654e91ed580ca7c51"}, - {file = "mypy-1.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:944bdc21ebd620eafefc090cdf83158393ec2b1391578359776c00de00e8907a"}, - {file = "mypy-1.7.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9c7ac372232c928fff0645d85f273a726970c014749b924ce5710d7d89763a28"}, - {file = "mypy-1.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:f6efc9bd72258f89a3816e3a98c09d36f079c223aa345c659622f056b760ab42"}, - {file = "mypy-1.7.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6dbdec441c60699288adf051f51a5d512b0d818526d1dcfff5a41f8cd8b4aaf1"}, - {file = "mypy-1.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4fc3d14ee80cd22367caaaf6e014494415bf440980a3045bf5045b525680ac33"}, - {file = "mypy-1.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c6e4464ed5f01dc44dc9821caf67b60a4e5c3b04278286a85c067010653a0eb"}, - {file = "mypy-1.7.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:d9b338c19fa2412f76e17525c1b4f2c687a55b156320acb588df79f2e6fa9fea"}, - {file = "mypy-1.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:204e0d6de5fd2317394a4eff62065614c4892d5a4d1a7ee55b765d7a3d9e3f82"}, - {file = "mypy-1.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:84860e06ba363d9c0eeabd45ac0fde4b903ad7aa4f93cd8b648385a888e23200"}, - {file = "mypy-1.7.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8c5091ebd294f7628eb25ea554852a52058ac81472c921150e3a61cdd68f75a7"}, - {file = "mypy-1.7.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40716d1f821b89838589e5b3106ebbc23636ffdef5abc31f7cd0266db936067e"}, - {file = "mypy-1.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5cf3f0c5ac72139797953bd50bc6c95ac13075e62dbfcc923571180bebb662e9"}, - {file = "mypy-1.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:78e25b2fd6cbb55ddfb8058417df193f0129cad5f4ee75d1502248e588d9e0d7"}, - {file = "mypy-1.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:75c4d2a6effd015786c87774e04331b6da863fc3fc4e8adfc3b40aa55ab516fe"}, - {file = "mypy-1.7.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2643d145af5292ee956aa0a83c2ce1038a3bdb26e033dadeb2f7066fb0c9abce"}, - {file = "mypy-1.7.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75aa828610b67462ffe3057d4d8a4112105ed211596b750b53cbfe182f44777a"}, - {file = "mypy-1.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ee5d62d28b854eb61889cde4e1dbc10fbaa5560cb39780c3995f6737f7e82120"}, - {file = "mypy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:72cf32ce7dd3562373f78bd751f73c96cfb441de147cc2448a92c1a308bd0ca6"}, - {file = "mypy-1.7.1-py3-none-any.whl", hash = "sha256:f7c5d642db47376a0cc130f0de6d055056e010debdaf0707cd2b0fc7e7ef30ea"}, - {file = "mypy-1.7.1.tar.gz", hash = "sha256:fcb6d9afb1b6208b4c712af0dafdc650f518836065df0d4fb1d800f5d6773db2"}, + {file = "mypy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3"}, + {file = "mypy-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4"}, + {file = "mypy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d"}, + {file = "mypy-1.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9"}, + {file = "mypy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410"}, + {file = "mypy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae"}, + {file = "mypy-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3"}, + {file = "mypy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817"}, + {file = "mypy-1.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d"}, + {file = "mypy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835"}, + {file = "mypy-1.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd"}, + {file = "mypy-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55"}, + {file = "mypy-1.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218"}, + {file = "mypy-1.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3"}, + {file = "mypy-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e"}, + {file = "mypy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6"}, + {file = "mypy-1.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66"}, + {file = "mypy-1.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6"}, + {file = "mypy-1.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d"}, + {file = "mypy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02"}, + {file = "mypy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8"}, + {file = "mypy-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259"}, + {file = "mypy-1.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b"}, + {file = "mypy-1.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592"}, + {file = "mypy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a"}, + {file = "mypy-1.8.0-py3-none-any.whl", hash = "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d"}, + {file = "mypy-1.8.0.tar.gz", hash = "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07"}, ] [package.dependencies] @@ -2296,70 +2283,88 @@ ptyprocess = ">=0.5" [[package]] name = "pillow" -version = "10.1.0" +version = "10.2.0" description = "Python Imaging Library (Fork)" optional = true python-versions = ">=3.8" files = [ - {file = "Pillow-10.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1ab05f3db77e98f93964697c8efc49c7954b08dd61cff526b7f2531a22410106"}, - {file = "Pillow-10.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6932a7652464746fcb484f7fc3618e6503d2066d853f68a4bd97193a3996e273"}, - {file = "Pillow-10.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f63b5a68daedc54c7c3464508d8c12075e56dcfbd42f8c1bf40169061ae666"}, - {file = "Pillow-10.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0949b55eb607898e28eaccb525ab104b2d86542a85c74baf3a6dc24002edec2"}, - {file = "Pillow-10.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ae88931f93214777c7a3aa0a8f92a683f83ecde27f65a45f95f22d289a69e593"}, - {file = "Pillow-10.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b0eb01ca85b2361b09480784a7931fc648ed8b7836f01fb9241141b968feb1db"}, - {file = "Pillow-10.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d27b5997bdd2eb9fb199982bb7eb6164db0426904020dc38c10203187ae2ff2f"}, - {file = "Pillow-10.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7df5608bc38bd37ef585ae9c38c9cd46d7c81498f086915b0f97255ea60c2818"}, - {file = "Pillow-10.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:41f67248d92a5e0a2076d3517d8d4b1e41a97e2df10eb8f93106c89107f38b57"}, - {file = "Pillow-10.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1fb29c07478e6c06a46b867e43b0bcdb241b44cc52be9bc25ce5944eed4648e7"}, - {file = "Pillow-10.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2cdc65a46e74514ce742c2013cd4a2d12e8553e3a2563c64879f7c7e4d28bce7"}, - {file = "Pillow-10.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50d08cd0a2ecd2a8657bd3d82c71efd5a58edb04d9308185d66c3a5a5bed9610"}, - {file = "Pillow-10.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:062a1610e3bc258bff2328ec43f34244fcec972ee0717200cb1425214fe5b839"}, - {file = "Pillow-10.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:61f1a9d247317fa08a308daaa8ee7b3f760ab1809ca2da14ecc88ae4257d6172"}, - {file = "Pillow-10.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a646e48de237d860c36e0db37ecaecaa3619e6f3e9d5319e527ccbc8151df061"}, - {file = "Pillow-10.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:47e5bf85b80abc03be7455c95b6d6e4896a62f6541c1f2ce77a7d2bb832af262"}, - {file = "Pillow-10.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a92386125e9ee90381c3369f57a2a50fa9e6aa8b1cf1d9c4b200d41a7dd8e992"}, - {file = "Pillow-10.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:0f7c276c05a9767e877a0b4c5050c8bee6a6d960d7f0c11ebda6b99746068c2a"}, - {file = "Pillow-10.1.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:a89b8312d51715b510a4fe9fc13686283f376cfd5abca8cd1c65e4c76e21081b"}, - {file = "Pillow-10.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:00f438bb841382b15d7deb9a05cc946ee0f2c352653c7aa659e75e592f6fa17d"}, - {file = "Pillow-10.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d929a19f5469b3f4df33a3df2983db070ebb2088a1e145e18facbc28cae5b27"}, - {file = "Pillow-10.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a92109192b360634a4489c0c756364c0c3a2992906752165ecb50544c251312"}, - {file = "Pillow-10.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:0248f86b3ea061e67817c47ecbe82c23f9dd5d5226200eb9090b3873d3ca32de"}, - {file = "Pillow-10.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9882a7451c680c12f232a422730f986a1fcd808da0fd428f08b671237237d651"}, - {file = "Pillow-10.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1c3ac5423c8c1da5928aa12c6e258921956757d976405e9467c5f39d1d577a4b"}, - {file = "Pillow-10.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:806abdd8249ba3953c33742506fe414880bad78ac25cc9a9b1c6ae97bedd573f"}, - {file = "Pillow-10.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:eaed6977fa73408b7b8a24e8b14e59e1668cfc0f4c40193ea7ced8e210adf996"}, - {file = "Pillow-10.1.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:fe1e26e1ffc38be097f0ba1d0d07fcade2bcfd1d023cda5b29935ae8052bd793"}, - {file = "Pillow-10.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7a7e3daa202beb61821c06d2517428e8e7c1aab08943e92ec9e5755c2fc9ba5e"}, - {file = "Pillow-10.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24fadc71218ad2b8ffe437b54876c9382b4a29e030a05a9879f615091f42ffc2"}, - {file = "Pillow-10.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa1d323703cfdac2036af05191b969b910d8f115cf53093125e4058f62012c9a"}, - {file = "Pillow-10.1.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:912e3812a1dbbc834da2b32299b124b5ddcb664ed354916fd1ed6f193f0e2d01"}, - {file = "Pillow-10.1.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:7dbaa3c7de82ef37e7708521be41db5565004258ca76945ad74a8e998c30af8d"}, - {file = "Pillow-10.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9d7bc666bd8c5a4225e7ac71f2f9d12466ec555e89092728ea0f5c0c2422ea80"}, - {file = "Pillow-10.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:baada14941c83079bf84c037e2d8b7506ce201e92e3d2fa0d1303507a8538212"}, - {file = "Pillow-10.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:2ef6721c97894a7aa77723740a09547197533146fba8355e86d6d9a4a1056b14"}, - {file = "Pillow-10.1.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0a026c188be3b443916179f5d04548092e253beb0c3e2ee0a4e2cdad72f66099"}, - {file = "Pillow-10.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:04f6f6149f266a100374ca3cc368b67fb27c4af9f1cc8cb6306d849dcdf12616"}, - {file = "Pillow-10.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb40c011447712d2e19cc261c82655f75f32cb724788df315ed992a4d65696bb"}, - {file = "Pillow-10.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a8413794b4ad9719346cd9306118450b7b00d9a15846451549314a58ac42219"}, - {file = "Pillow-10.1.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c9aeea7b63edb7884b031a35305629a7593272b54f429a9869a4f63a1bf04c34"}, - {file = "Pillow-10.1.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b4005fee46ed9be0b8fb42be0c20e79411533d1fd58edabebc0dd24626882cfd"}, - {file = "Pillow-10.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d0152565c6aa6ebbfb1e5d8624140a440f2b99bf7afaafbdbf6430426497f28"}, - {file = "Pillow-10.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d921bc90b1defa55c9917ca6b6b71430e4286fc9e44c55ead78ca1a9f9eba5f2"}, - {file = "Pillow-10.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cfe96560c6ce2f4c07d6647af2d0f3c54cc33289894ebd88cfbb3bcd5391e256"}, - {file = "Pillow-10.1.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:937bdc5a7f5343d1c97dc98149a0be7eb9704e937fe3dc7140e229ae4fc572a7"}, - {file = "Pillow-10.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1c25762197144e211efb5f4e8ad656f36c8d214d390585d1d21281f46d556ba"}, - {file = "Pillow-10.1.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:afc8eef765d948543a4775f00b7b8c079b3321d6b675dde0d02afa2ee23000b4"}, - {file = "Pillow-10.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:883f216eac8712b83a63f41b76ddfb7b2afab1b74abbb413c5df6680f071a6b9"}, - {file = "Pillow-10.1.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b920e4d028f6442bea9a75b7491c063f0b9a3972520731ed26c83e254302eb1e"}, - {file = "Pillow-10.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c41d960babf951e01a49c9746f92c5a7e0d939d1652d7ba30f6b3090f27e412"}, - {file = "Pillow-10.1.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1fafabe50a6977ac70dfe829b2d5735fd54e190ab55259ec8aea4aaea412fa0b"}, - {file = "Pillow-10.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3b834f4b16173e5b92ab6566f0473bfb09f939ba14b23b8da1f54fa63e4b623f"}, - {file = "Pillow-10.1.0.tar.gz", hash = "sha256:e6bf8de6c36ed96c86ea3b6e1d5273c53f46ef518a062464cd7ef5dd2cf92e38"}, + {file = "pillow-10.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:7823bdd049099efa16e4246bdf15e5a13dbb18a51b68fa06d6c1d4d8b99a796e"}, + {file = "pillow-10.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:83b2021f2ade7d1ed556bc50a399127d7fb245e725aa0113ebd05cfe88aaf588"}, + {file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fad5ff2f13d69b7e74ce5b4ecd12cc0ec530fcee76356cac6742785ff71c452"}, + {file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da2b52b37dad6d9ec64e653637a096905b258d2fc2b984c41ae7d08b938a67e4"}, + {file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:47c0995fc4e7f79b5cfcab1fc437ff2890b770440f7696a3ba065ee0fd496563"}, + {file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:322bdf3c9b556e9ffb18f93462e5f749d3444ce081290352c6070d014c93feb2"}, + {file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:51f1a1bffc50e2e9492e87d8e09a17c5eea8409cda8d3f277eb6edc82813c17c"}, + {file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69ffdd6120a4737710a9eee73e1d2e37db89b620f702754b8f6e62594471dee0"}, + {file = "pillow-10.2.0-cp310-cp310-win32.whl", hash = "sha256:c6dafac9e0f2b3c78df97e79af707cdc5ef8e88208d686a4847bab8266870023"}, + {file = "pillow-10.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:aebb6044806f2e16ecc07b2a2637ee1ef67a11840a66752751714a0d924adf72"}, + {file = "pillow-10.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:7049e301399273a0136ff39b84c3678e314f2158f50f517bc50285fb5ec847ad"}, + {file = "pillow-10.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:35bb52c37f256f662abdfa49d2dfa6ce5d93281d323a9af377a120e89a9eafb5"}, + {file = "pillow-10.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c23f307202661071d94b5e384e1e1dc7dfb972a28a2310e4ee16103e66ddb67"}, + {file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:773efe0603db30c281521a7c0214cad7836c03b8ccff897beae9b47c0b657d61"}, + {file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11fa2e5984b949b0dd6d7a94d967743d87c577ff0b83392f17cb3990d0d2fd6e"}, + {file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:716d30ed977be8b37d3ef185fecb9e5a1d62d110dfbdcd1e2a122ab46fddb03f"}, + {file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a086c2af425c5f62a65e12fbf385f7c9fcb8f107d0849dba5839461a129cf311"}, + {file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c8de2789052ed501dd829e9cae8d3dcce7acb4777ea4a479c14521c942d395b1"}, + {file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:609448742444d9290fd687940ac0b57fb35e6fd92bdb65386e08e99af60bf757"}, + {file = "pillow-10.2.0-cp311-cp311-win32.whl", hash = "sha256:823ef7a27cf86df6597fa0671066c1b596f69eba53efa3d1e1cb8b30f3533068"}, + {file = "pillow-10.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:1da3b2703afd040cf65ec97efea81cfba59cdbed9c11d8efc5ab09df9509fc56"}, + {file = "pillow-10.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:edca80cbfb2b68d7b56930b84a0e45ae1694aeba0541f798e908a49d66b837f1"}, + {file = "pillow-10.2.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:1b5e1b74d1bd1b78bc3477528919414874748dd363e6272efd5abf7654e68bef"}, + {file = "pillow-10.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0eae2073305f451d8ecacb5474997c08569fb4eb4ac231ffa4ad7d342fdc25ac"}, + {file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7c2286c23cd350b80d2fc9d424fc797575fb16f854b831d16fd47ceec078f2c"}, + {file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e23412b5c41e58cec602f1135c57dfcf15482013ce6e5f093a86db69646a5aa"}, + {file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:52a50aa3fb3acb9cf7213573ef55d31d6eca37f5709c69e6858fe3bc04a5c2a2"}, + {file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:127cee571038f252a552760076407f9cff79761c3d436a12af6000cd182a9d04"}, + {file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8d12251f02d69d8310b046e82572ed486685c38f02176bd08baf216746eb947f"}, + {file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:54f1852cd531aa981bc0965b7d609f5f6cc8ce8c41b1139f6ed6b3c54ab82bfb"}, + {file = "pillow-10.2.0-cp312-cp312-win32.whl", hash = "sha256:257d8788df5ca62c980314053197f4d46eefedf4e6175bc9412f14412ec4ea2f"}, + {file = "pillow-10.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:154e939c5f0053a383de4fd3d3da48d9427a7e985f58af8e94d0b3c9fcfcf4f9"}, + {file = "pillow-10.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:f379abd2f1e3dddb2b61bc67977a6b5a0a3f7485538bcc6f39ec76163891ee48"}, + {file = "pillow-10.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8373c6c251f7ef8bda6675dd6d2b3a0fcc31edf1201266b5cf608b62a37407f9"}, + {file = "pillow-10.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:870ea1ada0899fd0b79643990809323b389d4d1d46c192f97342eeb6ee0b8483"}, + {file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4b6b1e20608493548b1f32bce8cca185bf0480983890403d3b8753e44077129"}, + {file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3031709084b6e7852d00479fd1d310b07d0ba82765f973b543c8af5061cf990e"}, + {file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:3ff074fc97dd4e80543a3e91f69d58889baf2002b6be64347ea8cf5533188213"}, + {file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:cb4c38abeef13c61d6916f264d4845fab99d7b711be96c326b84df9e3e0ff62d"}, + {file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b1b3020d90c2d8e1dae29cf3ce54f8094f7938460fb5ce8bc5c01450b01fbaf6"}, + {file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:170aeb00224ab3dc54230c797f8404507240dd868cf52066f66a41b33169bdbe"}, + {file = "pillow-10.2.0-cp38-cp38-win32.whl", hash = "sha256:c4225f5220f46b2fde568c74fca27ae9771536c2e29d7c04f4fb62c83275ac4e"}, + {file = "pillow-10.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:0689b5a8c5288bc0504d9fcee48f61a6a586b9b98514d7d29b840143d6734f39"}, + {file = "pillow-10.2.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:b792a349405fbc0163190fde0dc7b3fef3c9268292586cf5645598b48e63dc67"}, + {file = "pillow-10.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c570f24be1e468e3f0ce7ef56a89a60f0e05b30a3669a459e419c6eac2c35364"}, + {file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8ecd059fdaf60c1963c58ceb8997b32e9dc1b911f5da5307aab614f1ce5c2fb"}, + {file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c365fd1703040de1ec284b176d6af5abe21b427cb3a5ff68e0759e1e313a5e7e"}, + {file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:70c61d4c475835a19b3a5aa42492409878bbca7438554a1f89d20d58a7c75c01"}, + {file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b6f491cdf80ae540738859d9766783e3b3c8e5bd37f5dfa0b76abdecc5081f13"}, + {file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d189550615b4948f45252d7f005e53c2040cea1af5b60d6f79491a6e147eef7"}, + {file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:49d9ba1ed0ef3e061088cd1e7538a0759aab559e2e0a80a36f9fd9d8c0c21591"}, + {file = "pillow-10.2.0-cp39-cp39-win32.whl", hash = "sha256:babf5acfede515f176833ed6028754cbcd0d206f7f614ea3447d67c33be12516"}, + {file = "pillow-10.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:0304004f8067386b477d20a518b50f3fa658a28d44e4116970abfcd94fac34a8"}, + {file = "pillow-10.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:0fb3e7fc88a14eacd303e90481ad983fd5b69c761e9e6ef94c983f91025da869"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:322209c642aabdd6207517e9739c704dc9f9db943015535783239022002f054a"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3eedd52442c0a5ff4f887fab0c1c0bb164d8635b32c894bc1faf4c618dd89df2"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb28c753fd5eb3dd859b4ee95de66cc62af91bcff5db5f2571d32a520baf1f04"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:33870dc4653c5017bf4c8873e5488d8f8d5f8935e2f1fb9a2208c47cdd66efd2"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3c31822339516fb3c82d03f30e22b1d038da87ef27b6a78c9549888f8ceda39a"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a2b56ba36e05f973d450582fb015594aaa78834fefe8dfb8fcd79b93e64ba4c6"}, + {file = "pillow-10.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d8e6aeb9201e655354b3ad049cb77d19813ad4ece0df1249d3c793de3774f8c7"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:2247178effb34a77c11c0e8ac355c7a741ceca0a732b27bf11e747bbc950722f"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15587643b9e5eb26c48e49a7b33659790d28f190fc514a322d55da2fb5c2950e"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753cd8f2086b2b80180d9b3010dd4ed147efc167c90d3bf593fe2af21265e5a5"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7c8f97e8e7a9009bcacbe3766a36175056c12f9a44e6e6f2d5caad06dcfbf03b"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d1b35bcd6c5543b9cb547dee3150c93008f8dd0f1fef78fc0cd2b141c5baf58a"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fe4c15f6c9285dc54ce6553a3ce908ed37c8f3825b5a51a15c91442bb955b868"}, + {file = "pillow-10.2.0.tar.gz", hash = "sha256:e87f0b2c78157e12d7686b27d63c070fd65d994e8ddae6f328e0dcf4a0cd007e"}, ] [package.extras] docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +typing = ["typing-extensions"] +xmp = ["defusedxml"] [[package]] name = "plantweb" @@ -2379,13 +2384,13 @@ six = "*" [[package]] name = "platformdirs" -version = "4.0.0" +version = "4.1.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "platformdirs-4.0.0-py3-none-any.whl", hash = "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b"}, - {file = "platformdirs-4.0.0.tar.gz", hash = "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731"}, + {file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"}, + {file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"}, ] [package.extras] @@ -2557,27 +2562,27 @@ dot = ["pydot (>=1.2.0)"] [[package]] name = "psutil" -version = "5.9.6" +version = "5.9.7" description = "Cross-platform lib for process and system monitoring in Python." optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ - {file = "psutil-5.9.6-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:fb8a697f11b0f5994550555fcfe3e69799e5b060c8ecf9e2f75c69302cc35c0d"}, - {file = "psutil-5.9.6-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:91ecd2d9c00db9817a4b4192107cf6954addb5d9d67a969a4f436dbc9200f88c"}, - {file = "psutil-5.9.6-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:10e8c17b4f898d64b121149afb136c53ea8b68c7531155147867b7b1ac9e7e28"}, - {file = "psutil-5.9.6-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:18cd22c5db486f33998f37e2bb054cc62fd06646995285e02a51b1e08da97017"}, - {file = "psutil-5.9.6-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:ca2780f5e038379e520281e4c032dddd086906ddff9ef0d1b9dcf00710e5071c"}, - {file = "psutil-5.9.6-cp27-none-win32.whl", hash = "sha256:70cb3beb98bc3fd5ac9ac617a327af7e7f826373ee64c80efd4eb2856e5051e9"}, - {file = "psutil-5.9.6-cp27-none-win_amd64.whl", hash = "sha256:51dc3d54607c73148f63732c727856f5febec1c7c336f8f41fcbd6315cce76ac"}, - {file = "psutil-5.9.6-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c69596f9fc2f8acd574a12d5f8b7b1ba3765a641ea5d60fb4736bf3c08a8214a"}, - {file = "psutil-5.9.6-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92e0cc43c524834af53e9d3369245e6cc3b130e78e26100d1f63cdb0abeb3d3c"}, - {file = "psutil-5.9.6-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:748c9dd2583ed86347ed65d0035f45fa8c851e8d90354c122ab72319b5f366f4"}, - {file = "psutil-5.9.6-cp36-cp36m-win32.whl", hash = "sha256:3ebf2158c16cc69db777e3c7decb3c0f43a7af94a60d72e87b2823aebac3d602"}, - {file = "psutil-5.9.6-cp36-cp36m-win_amd64.whl", hash = "sha256:ff18b8d1a784b810df0b0fff3bcb50ab941c3b8e2c8de5726f9c71c601c611aa"}, - {file = "psutil-5.9.6-cp37-abi3-win32.whl", hash = "sha256:a6f01f03bf1843280f4ad16f4bde26b817847b4c1a0db59bf6419807bc5ce05c"}, - {file = "psutil-5.9.6-cp37-abi3-win_amd64.whl", hash = "sha256:6e5fb8dc711a514da83098bc5234264e551ad980cec5f85dabf4d38ed6f15e9a"}, - {file = "psutil-5.9.6-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:daecbcbd29b289aac14ece28eca6a3e60aa361754cf6da3dfb20d4d32b6c7f57"}, - {file = "psutil-5.9.6.tar.gz", hash = "sha256:e4b92ddcd7dd4cdd3f900180ea1e104932c7bce234fb88976e2a3b296441225a"}, + {file = "psutil-5.9.7-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:0bd41bf2d1463dfa535942b2a8f0e958acf6607ac0be52265ab31f7923bcd5e6"}, + {file = "psutil-5.9.7-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:5794944462509e49d4d458f4dbfb92c47539e7d8d15c796f141f474010084056"}, + {file = "psutil-5.9.7-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:fe361f743cb3389b8efda21980d93eb55c1f1e3898269bc9a2a1d0bb7b1f6508"}, + {file = "psutil-5.9.7-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:e469990e28f1ad738f65a42dcfc17adaed9d0f325d55047593cb9033a0ab63df"}, + {file = "psutil-5.9.7-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:3c4747a3e2ead1589e647e64aad601981f01b68f9398ddf94d01e3dc0d1e57c7"}, + {file = "psutil-5.9.7-cp27-none-win32.whl", hash = "sha256:1d4bc4a0148fdd7fd8f38e0498639ae128e64538faa507df25a20f8f7fb2341c"}, + {file = "psutil-5.9.7-cp27-none-win_amd64.whl", hash = "sha256:4c03362e280d06bbbfcd52f29acd79c733e0af33d707c54255d21029b8b32ba6"}, + {file = "psutil-5.9.7-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ea36cc62e69a13ec52b2f625c27527f6e4479bca2b340b7a452af55b34fcbe2e"}, + {file = "psutil-5.9.7-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1132704b876e58d277168cd729d64750633d5ff0183acf5b3c986b8466cd0284"}, + {file = "psutil-5.9.7-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe8b7f07948f1304497ce4f4684881250cd859b16d06a1dc4d7941eeb6233bfe"}, + {file = "psutil-5.9.7-cp36-cp36m-win32.whl", hash = "sha256:b27f8fdb190c8c03914f908a4555159327d7481dac2f01008d483137ef3311a9"}, + {file = "psutil-5.9.7-cp36-cp36m-win_amd64.whl", hash = "sha256:44969859757f4d8f2a9bd5b76eba8c3099a2c8cf3992ff62144061e39ba8568e"}, + {file = "psutil-5.9.7-cp37-abi3-win32.whl", hash = "sha256:c727ca5a9b2dd5193b8644b9f0c883d54f1248310023b5ad3e92036c5e2ada68"}, + {file = "psutil-5.9.7-cp37-abi3-win_amd64.whl", hash = "sha256:f37f87e4d73b79e6c5e749440c3113b81d1ee7d26f21c19c47371ddea834f414"}, + {file = "psutil-5.9.7-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:032f4f2c909818c86cea4fe2cc407f1c0f0cde8e6c6d702b28b8ce0c0d143340"}, + {file = "psutil-5.9.7.tar.gz", hash = "sha256:3f02134e82cfb5d089fddf20bb2e03fd5cd52395321d1c8458a9e58500ff417c"}, ] [package.extras] @@ -2634,18 +2639,18 @@ files = [ [[package]] name = "pydantic" -version = "2.5.2" +version = "2.5.3" description = "Data validation using Python type hints" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic-2.5.2-py3-none-any.whl", hash = "sha256:80c50fb8e3dcecfddae1adbcc00ec5822918490c99ab31f6cf6140ca1c1429f0"}, - {file = "pydantic-2.5.2.tar.gz", hash = "sha256:ff177ba64c6faf73d7afa2e8cad38fd456c0dbe01c9954e71038001cd15a6edd"}, + {file = "pydantic-2.5.3-py3-none-any.whl", hash = "sha256:d0caf5954bee831b6bfe7e338c32b9e30c85dfe080c843680783ac2b631673b4"}, + {file = "pydantic-2.5.3.tar.gz", hash = "sha256:b3ef57c62535b0941697cce638c08900d87fcb67e29cfa99e8a68f747f393f7a"}, ] [package.dependencies] annotated-types = ">=0.4.0" -pydantic-core = "2.14.5" +pydantic-core = "2.14.6" typing-extensions = ">=4.6.1" [package.extras] @@ -2653,116 +2658,116 @@ email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" -version = "2.14.5" +version = "2.14.6" description = "" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic_core-2.14.5-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:7e88f5696153dc516ba6e79f82cc4747e87027205f0e02390c21f7cb3bd8abfd"}, - {file = "pydantic_core-2.14.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4641e8ad4efb697f38a9b64ca0523b557c7931c5f84e0fd377a9a3b05121f0de"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:774de879d212db5ce02dfbf5b0da9a0ea386aeba12b0b95674a4ce0593df3d07"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ebb4e035e28f49b6f1a7032920bb9a0c064aedbbabe52c543343d39341a5b2a3"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b53e9ad053cd064f7e473a5f29b37fc4cc9dc6d35f341e6afc0155ea257fc911"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aa1768c151cf562a9992462239dfc356b3d1037cc5a3ac829bb7f3bda7cc1f9"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eac5c82fc632c599f4639a5886f96867ffced74458c7db61bc9a66ccb8ee3113"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2ae91f50ccc5810b2f1b6b858257c9ad2e08da70bf890dee02de1775a387c66"}, - {file = "pydantic_core-2.14.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6b9ff467ffbab9110e80e8c8de3bcfce8e8b0fd5661ac44a09ae5901668ba997"}, - {file = "pydantic_core-2.14.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:61ea96a78378e3bd5a0be99b0e5ed00057b71f66115f5404d0dae4819f495093"}, - {file = "pydantic_core-2.14.5-cp310-none-win32.whl", hash = "sha256:bb4c2eda937a5e74c38a41b33d8c77220380a388d689bcdb9b187cf6224c9720"}, - {file = "pydantic_core-2.14.5-cp310-none-win_amd64.whl", hash = "sha256:b7851992faf25eac90bfcb7bfd19e1f5ffa00afd57daec8a0042e63c74a4551b"}, - {file = "pydantic_core-2.14.5-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:4e40f2bd0d57dac3feb3a3aed50f17d83436c9e6b09b16af271b6230a2915459"}, - {file = "pydantic_core-2.14.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ab1cdb0f14dc161ebc268c09db04d2c9e6f70027f3b42446fa11c153521c0e88"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aae7ea3a1c5bb40c93cad361b3e869b180ac174656120c42b9fadebf685d121b"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:60b7607753ba62cf0739177913b858140f11b8af72f22860c28eabb2f0a61937"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2248485b0322c75aee7565d95ad0e16f1c67403a470d02f94da7344184be770f"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:823fcc638f67035137a5cd3f1584a4542d35a951c3cc68c6ead1df7dac825c26"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96581cfefa9123accc465a5fd0cc833ac4d75d55cc30b633b402e00e7ced00a6"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a33324437018bf6ba1bb0f921788788641439e0ed654b233285b9c69704c27b4"}, - {file = "pydantic_core-2.14.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9bd18fee0923ca10f9a3ff67d4851c9d3e22b7bc63d1eddc12f439f436f2aada"}, - {file = "pydantic_core-2.14.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:853a2295c00f1d4429db4c0fb9475958543ee80cfd310814b5c0ef502de24dda"}, - {file = "pydantic_core-2.14.5-cp311-none-win32.whl", hash = "sha256:cb774298da62aea5c80a89bd58c40205ab4c2abf4834453b5de207d59d2e1651"}, - {file = "pydantic_core-2.14.5-cp311-none-win_amd64.whl", hash = "sha256:e87fc540c6cac7f29ede02e0f989d4233f88ad439c5cdee56f693cc9c1c78077"}, - {file = "pydantic_core-2.14.5-cp311-none-win_arm64.whl", hash = "sha256:57d52fa717ff445cb0a5ab5237db502e6be50809b43a596fb569630c665abddf"}, - {file = "pydantic_core-2.14.5-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:e60f112ac88db9261ad3a52032ea46388378034f3279c643499edb982536a093"}, - {file = "pydantic_core-2.14.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6e227c40c02fd873c2a73a98c1280c10315cbebe26734c196ef4514776120aeb"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0cbc7fff06a90bbd875cc201f94ef0ee3929dfbd5c55a06674b60857b8b85ed"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:103ef8d5b58596a731b690112819501ba1db7a36f4ee99f7892c40da02c3e189"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c949f04ecad823f81b1ba94e7d189d9dfb81edbb94ed3f8acfce41e682e48cef"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1452a1acdf914d194159439eb21e56b89aa903f2e1c65c60b9d874f9b950e5d"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb4679d4c2b089e5ef89756bc73e1926745e995d76e11925e3e96a76d5fa51fc"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf9d3fe53b1ee360e2421be95e62ca9b3296bf3f2fb2d3b83ca49ad3f925835e"}, - {file = "pydantic_core-2.14.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:70f4b4851dbb500129681d04cc955be2a90b2248d69273a787dda120d5cf1f69"}, - {file = "pydantic_core-2.14.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:59986de5710ad9613ff61dd9b02bdd2f615f1a7052304b79cc8fa2eb4e336d2d"}, - {file = "pydantic_core-2.14.5-cp312-none-win32.whl", hash = "sha256:699156034181e2ce106c89ddb4b6504c30db8caa86e0c30de47b3e0654543260"}, - {file = "pydantic_core-2.14.5-cp312-none-win_amd64.whl", hash = "sha256:5baab5455c7a538ac7e8bf1feec4278a66436197592a9bed538160a2e7d11e36"}, - {file = "pydantic_core-2.14.5-cp312-none-win_arm64.whl", hash = "sha256:e47e9a08bcc04d20975b6434cc50bf82665fbc751bcce739d04a3120428f3e27"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:af36f36538418f3806048f3b242a1777e2540ff9efaa667c27da63d2749dbce0"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:45e95333b8418ded64745f14574aa9bfc212cb4fbeed7a687b0c6e53b5e188cd"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e47a76848f92529879ecfc417ff88a2806438f57be4a6a8bf2961e8f9ca9ec7"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d81e6987b27bc7d101c8597e1cd2bcaa2fee5e8e0f356735c7ed34368c471550"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34708cc82c330e303f4ce87758828ef6e457681b58ce0e921b6e97937dd1e2a3"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:652c1988019752138b974c28f43751528116bcceadad85f33a258869e641d753"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e4d090e73e0725b2904fdbdd8d73b8802ddd691ef9254577b708d413bf3006e"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5c7d5b5005f177764e96bd584d7bf28d6e26e96f2a541fdddb934c486e36fd59"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a71891847f0a73b1b9eb86d089baee301477abef45f7eaf303495cd1473613e4"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a717aef6971208f0851a2420b075338e33083111d92041157bbe0e2713b37325"}, - {file = "pydantic_core-2.14.5-cp37-none-win32.whl", hash = "sha256:de790a3b5aa2124b8b78ae5faa033937a72da8efe74b9231698b5a1dd9be3405"}, - {file = "pydantic_core-2.14.5-cp37-none-win_amd64.whl", hash = "sha256:6c327e9cd849b564b234da821236e6bcbe4f359a42ee05050dc79d8ed2a91588"}, - {file = "pydantic_core-2.14.5-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:ef98ca7d5995a82f43ec0ab39c4caf6a9b994cb0b53648ff61716370eadc43cf"}, - {file = "pydantic_core-2.14.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6eae413494a1c3f89055da7a5515f32e05ebc1a234c27674a6956755fb2236f"}, - {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcf4e6d85614f7a4956c2de5a56531f44efb973d2fe4a444d7251df5d5c4dcfd"}, - {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6637560562134b0e17de333d18e69e312e0458ee4455bdad12c37100b7cad706"}, - {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77fa384d8e118b3077cccfcaf91bf83c31fe4dc850b5e6ee3dc14dc3d61bdba1"}, - {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16e29bad40bcf97aac682a58861249ca9dcc57c3f6be22f506501833ddb8939c"}, - {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531f4b4252fac6ca476fbe0e6f60f16f5b65d3e6b583bc4d87645e4e5ddde331"}, - {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:074f3d86f081ce61414d2dc44901f4f83617329c6f3ab49d2bc6c96948b2c26b"}, - {file = "pydantic_core-2.14.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c2adbe22ab4babbca99c75c5d07aaf74f43c3195384ec07ccbd2f9e3bddaecec"}, - {file = "pydantic_core-2.14.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0f6116a558fd06d1b7c2902d1c4cf64a5bd49d67c3540e61eccca93f41418124"}, - {file = "pydantic_core-2.14.5-cp38-none-win32.whl", hash = "sha256:fe0a5a1025eb797752136ac8b4fa21aa891e3d74fd340f864ff982d649691867"}, - {file = "pydantic_core-2.14.5-cp38-none-win_amd64.whl", hash = "sha256:079206491c435b60778cf2b0ee5fd645e61ffd6e70c47806c9ed51fc75af078d"}, - {file = "pydantic_core-2.14.5-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:a6a16f4a527aae4f49c875da3cdc9508ac7eef26e7977952608610104244e1b7"}, - {file = "pydantic_core-2.14.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:abf058be9517dc877227ec3223f0300034bd0e9f53aebd63cf4456c8cb1e0863"}, - {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49b08aae5013640a3bfa25a8eebbd95638ec3f4b2eaf6ed82cf0c7047133f03b"}, - {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c2d97e906b4ff36eb464d52a3bc7d720bd6261f64bc4bcdbcd2c557c02081ed2"}, - {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3128e0bbc8c091ec4375a1828d6118bc20404883169ac95ffa8d983b293611e6"}, - {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88e74ab0cdd84ad0614e2750f903bb0d610cc8af2cc17f72c28163acfcf372a4"}, - {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c339dabd8ee15f8259ee0f202679b6324926e5bc9e9a40bf981ce77c038553db"}, - {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3387277f1bf659caf1724e1afe8ee7dbc9952a82d90f858ebb931880216ea955"}, - {file = "pydantic_core-2.14.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ba6b6b3846cfc10fdb4c971980a954e49d447cd215ed5a77ec8190bc93dd7bc5"}, - {file = "pydantic_core-2.14.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ca61d858e4107ce5e1330a74724fe757fc7135190eb5ce5c9d0191729f033209"}, - {file = "pydantic_core-2.14.5-cp39-none-win32.whl", hash = "sha256:ec1e72d6412f7126eb7b2e3bfca42b15e6e389e1bc88ea0069d0cc1742f477c6"}, - {file = "pydantic_core-2.14.5-cp39-none-win_amd64.whl", hash = "sha256:c0b97ec434041827935044bbbe52b03d6018c2897349670ff8fe11ed24d1d4ab"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:79e0a2cdbdc7af3f4aee3210b1172ab53d7ddb6a2d8c24119b5706e622b346d0"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:678265f7b14e138d9a541ddabbe033012a2953315739f8cfa6d754cc8063e8ca"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95b15e855ae44f0c6341ceb74df61b606e11f1087e87dcb7482377374aac6abe"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09b0e985fbaf13e6b06a56d21694d12ebca6ce5414b9211edf6f17738d82b0f8"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3ad873900297bb36e4b6b3f7029d88ff9829ecdc15d5cf20161775ce12306f8a"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2d0ae0d8670164e10accbeb31d5ad45adb71292032d0fdb9079912907f0085f4"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d37f8ec982ead9ba0a22a996129594938138a1503237b87318392a48882d50b7"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:35613015f0ba7e14c29ac6c2483a657ec740e5ac5758d993fdd5870b07a61d8b"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab4ea451082e684198636565224bbb179575efc1658c48281b2c866bfd4ddf04"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ce601907e99ea5b4adb807ded3570ea62186b17f88e271569144e8cca4409c7"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb2ed8b3fe4bf4506d6dab3b93b83bbc22237e230cba03866d561c3577517d18"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:70f947628e074bb2526ba1b151cee10e4c3b9670af4dbb4d73bc8a89445916b5"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4bc536201426451f06f044dfbf341c09f540b4ebdb9fd8d2c6164d733de5e634"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4791cf0f8c3104ac668797d8c514afb3431bc3305f5638add0ba1a5a37e0d88"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:038c9f763e650712b899f983076ce783175397c848da04985658e7628cbe873b"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:27548e16c79702f1e03f5628589c6057c9ae17c95b4c449de3c66b589ead0520"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c97bee68898f3f4344eb02fec316db93d9700fb1e6a5b760ffa20d71d9a46ce3"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9b759b77f5337b4ea024f03abc6464c9f35d9718de01cfe6bae9f2e139c397e"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:439c9afe34638ace43a49bf72d201e0ffc1a800295bed8420c2a9ca8d5e3dbb3"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:ba39688799094c75ea8a16a6b544eb57b5b0f3328697084f3f2790892510d144"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ccd4d5702bb90b84df13bd491be8d900b92016c5a455b7e14630ad7449eb03f8"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:81982d78a45d1e5396819bbb4ece1fadfe5f079335dd28c4ab3427cd95389944"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:7f8210297b04e53bc3da35db08b7302a6a1f4889c79173af69b72ec9754796b8"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:8c8a8812fe6f43a3a5b054af6ac2d7b8605c7bcab2804a8a7d68b53f3cd86e00"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:206ed23aecd67c71daf5c02c3cd19c0501b01ef3cbf7782db9e4e051426b3d0d"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2027d05c8aebe61d898d4cffd774840a9cb82ed356ba47a90d99ad768f39789"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40180930807ce806aa71eda5a5a5447abb6b6a3c0b4b3b1b1962651906484d68"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:615a0a4bff11c45eb3c1996ceed5bdaa2f7b432425253a7c2eed33bb86d80abc"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5e412d717366e0677ef767eac93566582518fe8be923361a5c204c1a62eaafe"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:513b07e99c0a267b1d954243845d8a833758a6726a3b5d8948306e3fe14675e3"}, - {file = "pydantic_core-2.14.5.tar.gz", hash = "sha256:6d30226dfc816dd0fdf120cae611dd2215117e4f9b124af8c60ab9093b6e8e71"}, + {file = "pydantic_core-2.14.6-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:72f9a942d739f09cd42fffe5dc759928217649f070056f03c70df14f5770acf9"}, + {file = "pydantic_core-2.14.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6a31d98c0d69776c2576dda4b77b8e0c69ad08e8b539c25c7d0ca0dc19a50d6c"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5aa90562bc079c6c290f0512b21768967f9968e4cfea84ea4ff5af5d917016e4"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:370ffecb5316ed23b667d99ce4debe53ea664b99cc37bfa2af47bc769056d534"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f85f3843bdb1fe80e8c206fe6eed7a1caeae897e496542cee499c374a85c6e08"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9862bf828112e19685b76ca499b379338fd4c5c269d897e218b2ae8fcb80139d"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:036137b5ad0cb0004c75b579445a1efccd072387a36c7f217bb8efd1afbe5245"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92879bce89f91f4b2416eba4429c7b5ca22c45ef4a499c39f0c5c69257522c7c"}, + {file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0c08de15d50fa190d577e8591f0329a643eeaed696d7771760295998aca6bc66"}, + {file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:36099c69f6b14fc2c49d7996cbf4f87ec4f0e66d1c74aa05228583225a07b590"}, + {file = "pydantic_core-2.14.6-cp310-none-win32.whl", hash = "sha256:7be719e4d2ae6c314f72844ba9d69e38dff342bc360379f7c8537c48e23034b7"}, + {file = "pydantic_core-2.14.6-cp310-none-win_amd64.whl", hash = "sha256:36fa402dcdc8ea7f1b0ddcf0df4254cc6b2e08f8cd80e7010d4c4ae6e86b2a87"}, + {file = "pydantic_core-2.14.6-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:dea7fcd62915fb150cdc373212141a30037e11b761fbced340e9db3379b892d4"}, + {file = "pydantic_core-2.14.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffff855100bc066ff2cd3aa4a60bc9534661816b110f0243e59503ec2df38421"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b027c86c66b8627eb90e57aee1f526df77dc6d8b354ec498be9a757d513b92b"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:00b1087dabcee0b0ffd104f9f53d7d3eaddfaa314cdd6726143af6bc713aa27e"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75ec284328b60a4e91010c1acade0c30584f28a1f345bc8f72fe8b9e46ec6a96"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e1f4744eea1501404b20b0ac059ff7e3f96a97d3e3f48ce27a139e053bb370b"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2602177668f89b38b9f84b7b3435d0a72511ddef45dc14446811759b82235a1"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c8edaea3089bf908dd27da8f5d9e395c5b4dc092dbcce9b65e7156099b4b937"}, + {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:478e9e7b360dfec451daafe286998d4a1eeaecf6d69c427b834ae771cad4b622"}, + {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b6ca36c12a5120bad343eef193cc0122928c5c7466121da7c20f41160ba00ba2"}, + {file = "pydantic_core-2.14.6-cp311-none-win32.whl", hash = "sha256:2b8719037e570639e6b665a4050add43134d80b687288ba3ade18b22bbb29dd2"}, + {file = "pydantic_core-2.14.6-cp311-none-win_amd64.whl", hash = "sha256:78ee52ecc088c61cce32b2d30a826f929e1708f7b9247dc3b921aec367dc1b23"}, + {file = "pydantic_core-2.14.6-cp311-none-win_arm64.whl", hash = "sha256:a19b794f8fe6569472ff77602437ec4430f9b2b9ec7a1105cfd2232f9ba355e6"}, + {file = "pydantic_core-2.14.6-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:667aa2eac9cd0700af1ddb38b7b1ef246d8cf94c85637cbb03d7757ca4c3fdec"}, + {file = "pydantic_core-2.14.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdee837710ef6b56ebd20245b83799fce40b265b3b406e51e8ccc5b85b9099b7"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c5bcf3414367e29f83fd66f7de64509a8fd2368b1edf4351e862910727d3e51"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26a92ae76f75d1915806b77cf459811e772d8f71fd1e4339c99750f0e7f6324f"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a983cca5ed1dd9a35e9e42ebf9f278d344603bfcb174ff99a5815f953925140a"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cb92f9061657287eded380d7dc455bbf115430b3aa4741bdc662d02977e7d0af"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4ace1e220b078c8e48e82c081e35002038657e4b37d403ce940fa679e57113b"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef633add81832f4b56d3b4c9408b43d530dfca29e68fb1b797dcb861a2c734cd"}, + {file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7e90d6cc4aad2cc1f5e16ed56e46cebf4877c62403a311af20459c15da76fd91"}, + {file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e8a5ac97ea521d7bde7621d86c30e86b798cdecd985723c4ed737a2aa9e77d0c"}, + {file = "pydantic_core-2.14.6-cp312-none-win32.whl", hash = "sha256:f27207e8ca3e5e021e2402ba942e5b4c629718e665c81b8b306f3c8b1ddbb786"}, + {file = "pydantic_core-2.14.6-cp312-none-win_amd64.whl", hash = "sha256:b3e5fe4538001bb82e2295b8d2a39356a84694c97cb73a566dc36328b9f83b40"}, + {file = "pydantic_core-2.14.6-cp312-none-win_arm64.whl", hash = "sha256:64634ccf9d671c6be242a664a33c4acf12882670b09b3f163cd00a24cffbd74e"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:24368e31be2c88bd69340fbfe741b405302993242ccb476c5c3ff48aeee1afe0"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:e33b0834f1cf779aa839975f9d8755a7c2420510c0fa1e9fa0497de77cd35d2c"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6af4b3f52cc65f8a0bc8b1cd9676f8c21ef3e9132f21fed250f6958bd7223bed"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d15687d7d7f40333bd8266f3814c591c2e2cd263fa2116e314f60d82086e353a"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:095b707bb287bfd534044166ab767bec70a9bba3175dcdc3371782175c14e43c"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94fc0e6621e07d1e91c44e016cc0b189b48db053061cc22d6298a611de8071bb"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce830e480f6774608dedfd4a90c42aac4a7af0a711f1b52f807130c2e434c06"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a306cdd2ad3a7d795d8e617a58c3a2ed0f76c8496fb7621b6cd514eb1532cae8"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:2f5fa187bde8524b1e37ba894db13aadd64faa884657473b03a019f625cee9a8"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:438027a975cc213a47c5d70672e0d29776082155cfae540c4e225716586be75e"}, + {file = "pydantic_core-2.14.6-cp37-none-win32.whl", hash = "sha256:f96ae96a060a8072ceff4cfde89d261837b4294a4f28b84a28765470d502ccc6"}, + {file = "pydantic_core-2.14.6-cp37-none-win_amd64.whl", hash = "sha256:e646c0e282e960345314f42f2cea5e0b5f56938c093541ea6dbf11aec2862391"}, + {file = "pydantic_core-2.14.6-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:db453f2da3f59a348f514cfbfeb042393b68720787bbef2b4c6068ea362c8149"}, + {file = "pydantic_core-2.14.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3860c62057acd95cc84044e758e47b18dcd8871a328ebc8ccdefd18b0d26a21b"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36026d8f99c58d7044413e1b819a67ca0e0b8ebe0f25e775e6c3d1fabb3c38fb"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ed1af8692bd8d2a29d702f1a2e6065416d76897d726e45a1775b1444f5928a7"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:314ccc4264ce7d854941231cf71b592e30d8d368a71e50197c905874feacc8a8"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:982487f8931067a32e72d40ab6b47b1628a9c5d344be7f1a4e668fb462d2da42"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dbe357bc4ddda078f79d2a36fc1dd0494a7f2fad83a0a684465b6f24b46fe80"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2f6ffc6701a0eb28648c845f4945a194dc7ab3c651f535b81793251e1185ac3d"}, + {file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7f5025db12fc6de7bc1104d826d5aee1d172f9ba6ca936bf6474c2148ac336c1"}, + {file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dab03ed811ed1c71d700ed08bde8431cf429bbe59e423394f0f4055f1ca0ea60"}, + {file = "pydantic_core-2.14.6-cp38-none-win32.whl", hash = "sha256:dfcbebdb3c4b6f739a91769aea5ed615023f3c88cb70df812849aef634c25fbe"}, + {file = "pydantic_core-2.14.6-cp38-none-win_amd64.whl", hash = "sha256:99b14dbea2fdb563d8b5a57c9badfcd72083f6006caf8e126b491519c7d64ca8"}, + {file = "pydantic_core-2.14.6-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:4ce8299b481bcb68e5c82002b96e411796b844d72b3e92a3fbedfe8e19813eab"}, + {file = "pydantic_core-2.14.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b9a9d92f10772d2a181b5ca339dee066ab7d1c9a34ae2421b2a52556e719756f"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd9e98b408384989ea4ab60206b8e100d8687da18b5c813c11e92fd8212a98e0"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4f86f1f318e56f5cbb282fe61eb84767aee743ebe32c7c0834690ebea50c0a6b"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86ce5fcfc3accf3a07a729779d0b86c5d0309a4764c897d86c11089be61da160"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dcf1978be02153c6a31692d4fbcc2a3f1db9da36039ead23173bc256ee3b91b"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eedf97be7bc3dbc8addcef4142f4b4164066df0c6f36397ae4aaed3eb187d8ab"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5f916acf8afbcab6bacbb376ba7dc61f845367901ecd5e328fc4d4aef2fcab0"}, + {file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8a14c192c1d724c3acbfb3f10a958c55a2638391319ce8078cb36c02283959b9"}, + {file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0348b1dc6b76041516e8a854ff95b21c55f5a411c3297d2ca52f5528e49d8411"}, + {file = "pydantic_core-2.14.6-cp39-none-win32.whl", hash = "sha256:de2a0645a923ba57c5527497daf8ec5df69c6eadf869e9cd46e86349146e5975"}, + {file = "pydantic_core-2.14.6-cp39-none-win_amd64.whl", hash = "sha256:aca48506a9c20f68ee61c87f2008f81f8ee99f8d7f0104bff3c47e2d148f89d9"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d5c28525c19f5bb1e09511669bb57353d22b94cf8b65f3a8d141c389a55dec95"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:78d0768ee59baa3de0f4adac9e3748b4b1fffc52143caebddfd5ea2961595277"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b93785eadaef932e4fe9c6e12ba67beb1b3f1e5495631419c784ab87e975670"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a874f21f87c485310944b2b2734cd6d318765bcbb7515eead33af9641816506e"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89f4477d915ea43b4ceea6756f63f0288941b6443a2b28c69004fe07fde0d0d"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:172de779e2a153d36ee690dbc49c6db568d7b33b18dc56b69a7514aecbcf380d"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:dfcebb950aa7e667ec226a442722134539e77c575f6cfaa423f24371bb8d2e94"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:55a23dcd98c858c0db44fc5c04fc7ed81c4b4d33c653a7c45ddaebf6563a2f66"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:4241204e4b36ab5ae466ecec5c4c16527a054c69f99bba20f6f75232a6a534e2"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e574de99d735b3fc8364cba9912c2bec2da78775eba95cbb225ef7dda6acea24"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1302a54f87b5cd8528e4d6d1bf2133b6aa7c6122ff8e9dc5220fbc1e07bffebd"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8e81e4b55930e5ffab4a68db1af431629cf2e4066dbdbfef65348b8ab804ea8"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c99462ffc538717b3e60151dfaf91125f637e801f5ab008f81c402f1dff0cd0f"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e4cf2d5829f6963a5483ec01578ee76d329eb5caf330ecd05b3edd697e7d768a"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:cf10b7d58ae4a1f07fccbf4a0a956d705356fea05fb4c70608bb6fa81d103cda"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:399ac0891c284fa8eb998bcfa323f2234858f5d2efca3950ae58c8f88830f145"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c6a5c79b28003543db3ba67d1df336f253a87d3112dac3a51b94f7d48e4c0e1"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:599c87d79cab2a6a2a9df4aefe0455e61e7d2aeede2f8577c1b7c0aec643ee8e"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43e166ad47ba900f2542a80d83f9fc65fe99eb63ceec4debec160ae729824052"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3a0b5db001b98e1c649dd55afa928e75aa4087e587b9524a4992316fa23c9fba"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:747265448cb57a9f37572a488a57d873fd96bf51e5bb7edb52cfb37124516da4"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7ebe3416785f65c28f4f9441e916bfc8a54179c8dea73c23023f7086fa601c5d"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:86c963186ca5e50d5c8287b1d1c9d3f8f024cbe343d048c5bd282aec2d8641f2"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e0641b506486f0b4cd1500a2a65740243e8670a2549bb02bc4556a83af84ae03"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71d72ca5eaaa8d38c8df16b7deb1a2da4f650c41b58bb142f3fb75d5ad4a611f"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27e524624eace5c59af499cd97dc18bb201dc6a7a2da24bfc66ef151c69a5f2a"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3dde6cac75e0b0902778978d3b1646ca9f438654395a362cb21d9ad34b24acf"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:00646784f6cd993b1e1c0e7b0fdcbccc375d539db95555477771c27555e3c556"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:23598acb8ccaa3d1d875ef3b35cb6376535095e9405d91a3d57a8c7db5d29341"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7f41533d7e3cf9520065f610b41ac1c76bc2161415955fbcead4981b22c7611e"}, + {file = "pydantic_core-2.14.6.tar.gz", hash = "sha256:1fd0c1d395372843fba13a51c28e3bb9d59bd7aebfeb17358ffaaa1e4dbbe948"}, ] [package.dependencies] @@ -2787,17 +2792,22 @@ toml = ["tomli (>=1.2.3)"] [[package]] name = "pydot" -version = "1.4.2" +version = "2.0.0" description = "Python interface to Graphviz's Dot" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.7" files = [ - {file = "pydot-1.4.2-py2.py3-none-any.whl", hash = "sha256:66c98190c65b8d2e2382a441b4c0edfdb4f4c025ef9cb9874de478fb0793a451"}, - {file = "pydot-1.4.2.tar.gz", hash = "sha256:248081a39bcb56784deb018977e428605c1c758f10897a339fce1dd728ff007d"}, + {file = "pydot-2.0.0-py3-none-any.whl", hash = "sha256:408a47913ea7bd5d2d34b274144880c1310c4aee901f353cf21fe2e526a4ea28"}, + {file = "pydot-2.0.0.tar.gz", hash = "sha256:60246af215123fa062f21cd791be67dda23a6f280df09f68919e637a1e4f3235"}, ] [package.dependencies] -pyparsing = ">=2.1.4" +pyparsing = ">=3" + +[package.extras] +dev = ["black", "chardet"] +release = ["zest.releaser[recommended]"] +tests = ["black", "chardet", "tox"] [[package]] name = "pyenchant" @@ -2953,13 +2963,13 @@ wcwidth = "*" [[package]] name = "pytest" -version = "7.4.3" +version = "7.4.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.4.3-py3-none-any.whl", hash = "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac"}, - {file = "pytest-7.4.3.tar.gz", hash = "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"}, + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, ] [package.dependencies] @@ -3083,13 +3093,13 @@ pytest-cache = "*" [[package]] name = "pytest-recording" -version = "0.13.0" +version = "0.13.1" description = "A pytest plugin that allows you recording of network interactions via VCR.py" optional = false python-versions = ">=3.7" files = [ - {file = "pytest_recording-0.13.0-py3-none-any.whl", hash = "sha256:679b0ae6eb3337b981f6a4d590a30c28c43855bfac5b9ad87070ad6d08b05dbc"}, - {file = "pytest_recording-0.13.0.tar.gz", hash = "sha256:b24b707af843341457d9d340328f361eceb0efe980e388341941b4fada3745ca"}, + {file = "pytest_recording-0.13.1-py3-none-any.whl", hash = "sha256:e5c75feb2593eb4ed9362182c6640bfe19004204bf9a6082d62c91b5fdb50a3e"}, + {file = "pytest_recording-0.13.1.tar.gz", hash = "sha256:1265d679f39263f115968ec01c2a3bfed250170fd1b0d9e288970b2e4a13737a"}, ] [package.dependencies] @@ -3098,7 +3108,7 @@ vcrpy = ">=2.0.1" [package.extras] dev = ["pytest-recording[tests]"] -tests = ["pytest-httpbin", "pytest-mock", "requests", "werkzeug (==2.3.6)"] +tests = ["pytest-httpbin", "pytest-mock", "requests", "werkzeug (==3.0.1)"] [[package]] name = "pytest-timeout" @@ -3274,104 +3284,104 @@ files = [ [[package]] name = "pyzmq" -version = "25.1.1" +version = "25.1.2" description = "Python bindings for 0MQ" optional = true python-versions = ">=3.6" files = [ - {file = "pyzmq-25.1.1-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:381469297409c5adf9a0e884c5eb5186ed33137badcbbb0560b86e910a2f1e76"}, - {file = "pyzmq-25.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:955215ed0604dac5b01907424dfa28b40f2b2292d6493445dd34d0dfa72586a8"}, - {file = "pyzmq-25.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:985bbb1316192b98f32e25e7b9958088431d853ac63aca1d2c236f40afb17c83"}, - {file = "pyzmq-25.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:afea96f64efa98df4da6958bae37f1cbea7932c35878b185e5982821bc883369"}, - {file = "pyzmq-25.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76705c9325d72a81155bb6ab48d4312e0032bf045fb0754889133200f7a0d849"}, - {file = "pyzmq-25.1.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:77a41c26205d2353a4c94d02be51d6cbdf63c06fbc1295ea57dad7e2d3381b71"}, - {file = "pyzmq-25.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:12720a53e61c3b99d87262294e2b375c915fea93c31fc2336898c26d7aed34cd"}, - {file = "pyzmq-25.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:57459b68e5cd85b0be8184382cefd91959cafe79ae019e6b1ae6e2ba8a12cda7"}, - {file = "pyzmq-25.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:292fe3fc5ad4a75bc8df0dfaee7d0babe8b1f4ceb596437213821f761b4589f9"}, - {file = "pyzmq-25.1.1-cp310-cp310-win32.whl", hash = "sha256:35b5ab8c28978fbbb86ea54958cd89f5176ce747c1fb3d87356cf698048a7790"}, - {file = "pyzmq-25.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:11baebdd5fc5b475d484195e49bae2dc64b94a5208f7c89954e9e354fc609d8f"}, - {file = "pyzmq-25.1.1-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:d20a0ddb3e989e8807d83225a27e5c2eb2260eaa851532086e9e0fa0d5287d83"}, - {file = "pyzmq-25.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e1c1be77bc5fb77d923850f82e55a928f8638f64a61f00ff18a67c7404faf008"}, - {file = "pyzmq-25.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d89528b4943d27029a2818f847c10c2cecc79fa9590f3cb1860459a5be7933eb"}, - {file = "pyzmq-25.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:90f26dc6d5f241ba358bef79be9ce06de58d477ca8485e3291675436d3827cf8"}, - {file = "pyzmq-25.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2b92812bd214018e50b6380ea3ac0c8bb01ac07fcc14c5f86a5bb25e74026e9"}, - {file = "pyzmq-25.1.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:2f957ce63d13c28730f7fd6b72333814221c84ca2421298f66e5143f81c9f91f"}, - {file = "pyzmq-25.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:047a640f5c9c6ade7b1cc6680a0e28c9dd5a0825135acbd3569cc96ea00b2505"}, - {file = "pyzmq-25.1.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7f7e58effd14b641c5e4dec8c7dab02fb67a13df90329e61c869b9cc607ef752"}, - {file = "pyzmq-25.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c2910967e6ab16bf6fbeb1f771c89a7050947221ae12a5b0b60f3bca2ee19bca"}, - {file = "pyzmq-25.1.1-cp311-cp311-win32.whl", hash = "sha256:76c1c8efb3ca3a1818b837aea423ff8a07bbf7aafe9f2f6582b61a0458b1a329"}, - {file = "pyzmq-25.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:44e58a0554b21fc662f2712814a746635ed668d0fbc98b7cb9d74cb798d202e6"}, - {file = "pyzmq-25.1.1-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:e1ffa1c924e8c72778b9ccd386a7067cddf626884fd8277f503c48bb5f51c762"}, - {file = "pyzmq-25.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1af379b33ef33757224da93e9da62e6471cf4a66d10078cf32bae8127d3d0d4a"}, - {file = "pyzmq-25.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cff084c6933680d1f8b2f3b4ff5bbb88538a4aac00d199ac13f49d0698727ecb"}, - {file = "pyzmq-25.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2400a94f7dd9cb20cd012951a0cbf8249e3d554c63a9c0cdfd5cbb6c01d2dec"}, - {file = "pyzmq-25.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d81f1ddae3858b8299d1da72dd7d19dd36aab654c19671aa8a7e7fb02f6638a"}, - {file = "pyzmq-25.1.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:255ca2b219f9e5a3a9ef3081512e1358bd4760ce77828e1028b818ff5610b87b"}, - {file = "pyzmq-25.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a882ac0a351288dd18ecae3326b8a49d10c61a68b01419f3a0b9a306190baf69"}, - {file = "pyzmq-25.1.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:724c292bb26365659fc434e9567b3f1adbdb5e8d640c936ed901f49e03e5d32e"}, - {file = "pyzmq-25.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ca1ed0bb2d850aa8471387882247c68f1e62a4af0ce9c8a1dbe0d2bf69e41fb"}, - {file = "pyzmq-25.1.1-cp312-cp312-win32.whl", hash = "sha256:b3451108ab861040754fa5208bca4a5496c65875710f76789a9ad27c801a0075"}, - {file = "pyzmq-25.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:eadbefd5e92ef8a345f0525b5cfd01cf4e4cc651a2cffb8f23c0dd184975d787"}, - {file = "pyzmq-25.1.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:db0b2af416ba735c6304c47f75d348f498b92952f5e3e8bff449336d2728795d"}, - {file = "pyzmq-25.1.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c133e93b405eb0d36fa430c94185bdd13c36204a8635470cccc200723c13bb"}, - {file = "pyzmq-25.1.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:273bc3959bcbff3f48606b28229b4721716598d76b5aaea2b4a9d0ab454ec062"}, - {file = "pyzmq-25.1.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cbc8df5c6a88ba5ae385d8930da02201165408dde8d8322072e3e5ddd4f68e22"}, - {file = "pyzmq-25.1.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:18d43df3f2302d836f2a56f17e5663e398416e9dd74b205b179065e61f1a6edf"}, - {file = "pyzmq-25.1.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:73461eed88a88c866656e08f89299720a38cb4e9d34ae6bf5df6f71102570f2e"}, - {file = "pyzmq-25.1.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:34c850ce7976d19ebe7b9d4b9bb8c9dfc7aac336c0958e2651b88cbd46682123"}, - {file = "pyzmq-25.1.1-cp36-cp36m-win32.whl", hash = "sha256:d2045d6d9439a0078f2a34b57c7b18c4a6aef0bee37f22e4ec9f32456c852c71"}, - {file = "pyzmq-25.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:458dea649f2f02a0b244ae6aef8dc29325a2810aa26b07af8374dc2a9faf57e3"}, - {file = "pyzmq-25.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7cff25c5b315e63b07a36f0c2bab32c58eafbe57d0dce61b614ef4c76058c115"}, - {file = "pyzmq-25.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1579413ae492b05de5a6174574f8c44c2b9b122a42015c5292afa4be2507f28"}, - {file = "pyzmq-25.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3d0a409d3b28607cc427aa5c30a6f1e4452cc44e311f843e05edb28ab5e36da0"}, - {file = "pyzmq-25.1.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:21eb4e609a154a57c520e3d5bfa0d97e49b6872ea057b7c85257b11e78068222"}, - {file = "pyzmq-25.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:034239843541ef7a1aee0c7b2cb7f6aafffb005ede965ae9cbd49d5ff4ff73cf"}, - {file = "pyzmq-25.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f8115e303280ba09f3898194791a153862cbf9eef722ad8f7f741987ee2a97c7"}, - {file = "pyzmq-25.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1a5d26fe8f32f137e784f768143728438877d69a586ddeaad898558dc971a5ae"}, - {file = "pyzmq-25.1.1-cp37-cp37m-win32.whl", hash = "sha256:f32260e556a983bc5c7ed588d04c942c9a8f9c2e99213fec11a031e316874c7e"}, - {file = "pyzmq-25.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:abf34e43c531bbb510ae7e8f5b2b1f2a8ab93219510e2b287a944432fad135f3"}, - {file = "pyzmq-25.1.1-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:87e34f31ca8f168c56d6fbf99692cc8d3b445abb5bfd08c229ae992d7547a92a"}, - {file = "pyzmq-25.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c9c6c9b2c2f80747a98f34ef491c4d7b1a8d4853937bb1492774992a120f475d"}, - {file = "pyzmq-25.1.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5619f3f5a4db5dbb572b095ea3cb5cc035335159d9da950830c9c4db2fbb6995"}, - {file = "pyzmq-25.1.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5a34d2395073ef862b4032343cf0c32a712f3ab49d7ec4f42c9661e0294d106f"}, - {file = "pyzmq-25.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25f0e6b78220aba09815cd1f3a32b9c7cb3e02cb846d1cfc526b6595f6046618"}, - {file = "pyzmq-25.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3669cf8ee3520c2f13b2e0351c41fea919852b220988d2049249db10046a7afb"}, - {file = "pyzmq-25.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2d163a18819277e49911f7461567bda923461c50b19d169a062536fffe7cd9d2"}, - {file = "pyzmq-25.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:df27ffddff4190667d40de7beba4a950b5ce78fe28a7dcc41d6f8a700a80a3c0"}, - {file = "pyzmq-25.1.1-cp38-cp38-win32.whl", hash = "sha256:a382372898a07479bd34bda781008e4a954ed8750f17891e794521c3e21c2e1c"}, - {file = "pyzmq-25.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:52533489f28d62eb1258a965f2aba28a82aa747202c8fa5a1c7a43b5db0e85c1"}, - {file = "pyzmq-25.1.1-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:03b3f49b57264909aacd0741892f2aecf2f51fb053e7d8ac6767f6c700832f45"}, - {file = "pyzmq-25.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:330f9e188d0d89080cde66dc7470f57d1926ff2fb5576227f14d5be7ab30b9fa"}, - {file = "pyzmq-25.1.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2ca57a5be0389f2a65e6d3bb2962a971688cbdd30b4c0bd188c99e39c234f414"}, - {file = "pyzmq-25.1.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d457aed310f2670f59cc5b57dcfced452aeeed77f9da2b9763616bd57e4dbaae"}, - {file = "pyzmq-25.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c56d748ea50215abef7030c72b60dd723ed5b5c7e65e7bc2504e77843631c1a6"}, - {file = "pyzmq-25.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8f03d3f0d01cb5a018debeb412441996a517b11c5c17ab2001aa0597c6d6882c"}, - {file = "pyzmq-25.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:820c4a08195a681252f46926de10e29b6bbf3e17b30037bd4250d72dd3ddaab8"}, - {file = "pyzmq-25.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:17ef5f01d25b67ca8f98120d5fa1d21efe9611604e8eb03a5147360f517dd1e2"}, - {file = "pyzmq-25.1.1-cp39-cp39-win32.whl", hash = "sha256:04ccbed567171579ec2cebb9c8a3e30801723c575601f9a990ab25bcac6b51e2"}, - {file = "pyzmq-25.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:e61f091c3ba0c3578411ef505992d356a812fb200643eab27f4f70eed34a29ef"}, - {file = "pyzmq-25.1.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ade6d25bb29c4555d718ac6d1443a7386595528c33d6b133b258f65f963bb0f6"}, - {file = "pyzmq-25.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0c95ddd4f6e9fca4e9e3afaa4f9df8552f0ba5d1004e89ef0a68e1f1f9807c7"}, - {file = "pyzmq-25.1.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48e466162a24daf86f6b5ca72444d2bf39a5e58da5f96370078be67c67adc978"}, - {file = "pyzmq-25.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abc719161780932c4e11aaebb203be3d6acc6b38d2f26c0f523b5b59d2fc1996"}, - {file = "pyzmq-25.1.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1ccf825981640b8c34ae54231b7ed00271822ea1c6d8ba1090ebd4943759abf5"}, - {file = "pyzmq-25.1.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c2f20ce161ebdb0091a10c9ca0372e023ce24980d0e1f810f519da6f79c60800"}, - {file = "pyzmq-25.1.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:deee9ca4727f53464daf089536e68b13e6104e84a37820a88b0a057b97bba2d2"}, - {file = "pyzmq-25.1.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:aa8d6cdc8b8aa19ceb319aaa2b660cdaccc533ec477eeb1309e2a291eaacc43a"}, - {file = "pyzmq-25.1.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:019e59ef5c5256a2c7378f2fb8560fc2a9ff1d315755204295b2eab96b254d0a"}, - {file = "pyzmq-25.1.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:b9af3757495c1ee3b5c4e945c1df7be95562277c6e5bccc20a39aec50f826cd0"}, - {file = "pyzmq-25.1.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:548d6482dc8aadbe7e79d1b5806585c8120bafa1ef841167bc9090522b610fa6"}, - {file = "pyzmq-25.1.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:057e824b2aae50accc0f9a0570998adc021b372478a921506fddd6c02e60308e"}, - {file = "pyzmq-25.1.1-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2243700cc5548cff20963f0ca92d3e5e436394375ab8a354bbea2b12911b20b0"}, - {file = "pyzmq-25.1.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79986f3b4af059777111409ee517da24a529bdbd46da578b33f25580adcff728"}, - {file = "pyzmq-25.1.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:11d58723d44d6ed4dd677c5615b2ffb19d5c426636345567d6af82be4dff8a55"}, - {file = "pyzmq-25.1.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:49d238cf4b69652257db66d0c623cd3e09b5d2e9576b56bc067a396133a00d4a"}, - {file = "pyzmq-25.1.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fedbdc753827cf014c01dbbee9c3be17e5a208dcd1bf8641ce2cd29580d1f0d4"}, - {file = "pyzmq-25.1.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc16ac425cc927d0a57d242589f87ee093884ea4804c05a13834d07c20db203c"}, - {file = "pyzmq-25.1.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11c1d2aed9079c6b0c9550a7257a836b4a637feb334904610f06d70eb44c56d2"}, - {file = "pyzmq-25.1.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e8a701123029cc240cea61dd2d16ad57cab4691804143ce80ecd9286b464d180"}, - {file = "pyzmq-25.1.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:61706a6b6c24bdece85ff177fec393545a3191eeda35b07aaa1458a027ad1304"}, - {file = "pyzmq-25.1.1.tar.gz", hash = "sha256:259c22485b71abacdfa8bf79720cd7bcf4b9d128b30ea554f01ae71fdbfdaa23"}, + {file = "pyzmq-25.1.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:e624c789359f1a16f83f35e2c705d07663ff2b4d4479bad35621178d8f0f6ea4"}, + {file = "pyzmq-25.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:49151b0efece79f6a79d41a461d78535356136ee70084a1c22532fc6383f4ad0"}, + {file = "pyzmq-25.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9a5f194cf730f2b24d6af1f833c14c10f41023da46a7f736f48b6d35061e76e"}, + {file = "pyzmq-25.1.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:faf79a302f834d9e8304fafdc11d0d042266667ac45209afa57e5efc998e3872"}, + {file = "pyzmq-25.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f51a7b4ead28d3fca8dda53216314a553b0f7a91ee8fc46a72b402a78c3e43d"}, + {file = "pyzmq-25.1.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0ddd6d71d4ef17ba5a87becf7ddf01b371eaba553c603477679ae817a8d84d75"}, + {file = "pyzmq-25.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:246747b88917e4867e2367b005fc8eefbb4a54b7db363d6c92f89d69abfff4b6"}, + {file = "pyzmq-25.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:00c48ae2fd81e2a50c3485de1b9d5c7c57cd85dc8ec55683eac16846e57ac979"}, + {file = "pyzmq-25.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5a68d491fc20762b630e5db2191dd07ff89834086740f70e978bb2ef2668be08"}, + {file = "pyzmq-25.1.2-cp310-cp310-win32.whl", hash = "sha256:09dfe949e83087da88c4a76767df04b22304a682d6154de2c572625c62ad6886"}, + {file = "pyzmq-25.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:fa99973d2ed20417744fca0073390ad65ce225b546febb0580358e36aa90dba6"}, + {file = "pyzmq-25.1.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:82544e0e2d0c1811482d37eef297020a040c32e0687c1f6fc23a75b75db8062c"}, + {file = "pyzmq-25.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:01171fc48542348cd1a360a4b6c3e7d8f46cdcf53a8d40f84db6707a6768acc1"}, + {file = "pyzmq-25.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc69c96735ab501419c432110016329bf0dea8898ce16fab97c6d9106dc0b348"}, + {file = "pyzmq-25.1.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3e124e6b1dd3dfbeb695435dff0e383256655bb18082e094a8dd1f6293114642"}, + {file = "pyzmq-25.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7598d2ba821caa37a0f9d54c25164a4fa351ce019d64d0b44b45540950458840"}, + {file = "pyzmq-25.1.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d1299d7e964c13607efd148ca1f07dcbf27c3ab9e125d1d0ae1d580a1682399d"}, + {file = "pyzmq-25.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4e6f689880d5ad87918430957297c975203a082d9a036cc426648fcbedae769b"}, + {file = "pyzmq-25.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cc69949484171cc961e6ecd4a8911b9ce7a0d1f738fcae717177c231bf77437b"}, + {file = "pyzmq-25.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9880078f683466b7f567b8624bfc16cad65077be046b6e8abb53bed4eeb82dd3"}, + {file = "pyzmq-25.1.2-cp311-cp311-win32.whl", hash = "sha256:4e5837af3e5aaa99a091302df5ee001149baff06ad22b722d34e30df5f0d9097"}, + {file = "pyzmq-25.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:25c2dbb97d38b5ac9fd15586e048ec5eb1e38f3d47fe7d92167b0c77bb3584e9"}, + {file = "pyzmq-25.1.2-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:11e70516688190e9c2db14fcf93c04192b02d457b582a1f6190b154691b4c93a"}, + {file = "pyzmq-25.1.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:313c3794d650d1fccaaab2df942af9f2c01d6217c846177cfcbc693c7410839e"}, + {file = "pyzmq-25.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b3cbba2f47062b85fe0ef9de5b987612140a9ba3a9c6d2543c6dec9f7c2ab27"}, + {file = "pyzmq-25.1.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc31baa0c32a2ca660784d5af3b9487e13b61b3032cb01a115fce6588e1bed30"}, + {file = "pyzmq-25.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02c9087b109070c5ab0b383079fa1b5f797f8d43e9a66c07a4b8b8bdecfd88ee"}, + {file = "pyzmq-25.1.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f8429b17cbb746c3e043cb986328da023657e79d5ed258b711c06a70c2ea7537"}, + {file = "pyzmq-25.1.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5074adeacede5f810b7ef39607ee59d94e948b4fd954495bdb072f8c54558181"}, + {file = "pyzmq-25.1.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7ae8f354b895cbd85212da245f1a5ad8159e7840e37d78b476bb4f4c3f32a9fe"}, + {file = "pyzmq-25.1.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b264bf2cc96b5bc43ce0e852be995e400376bd87ceb363822e2cb1964fcdc737"}, + {file = "pyzmq-25.1.2-cp312-cp312-win32.whl", hash = "sha256:02bbc1a87b76e04fd780b45e7f695471ae6de747769e540da909173d50ff8e2d"}, + {file = "pyzmq-25.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:ced111c2e81506abd1dc142e6cd7b68dd53747b3b7ae5edbea4578c5eeff96b7"}, + {file = "pyzmq-25.1.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:7b6d09a8962a91151f0976008eb7b29b433a560fde056ec7a3db9ec8f1075438"}, + {file = "pyzmq-25.1.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:967668420f36878a3c9ecb5ab33c9d0ff8d054f9c0233d995a6d25b0e95e1b6b"}, + {file = "pyzmq-25.1.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5edac3f57c7ddaacdb4d40f6ef2f9e299471fc38d112f4bc6d60ab9365445fb0"}, + {file = "pyzmq-25.1.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:0dabfb10ef897f3b7e101cacba1437bd3a5032ee667b7ead32bbcdd1a8422fe7"}, + {file = "pyzmq-25.1.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:2c6441e0398c2baacfe5ba30c937d274cfc2dc5b55e82e3749e333aabffde561"}, + {file = "pyzmq-25.1.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:16b726c1f6c2e7625706549f9dbe9b06004dfbec30dbed4bf50cbdfc73e5b32a"}, + {file = "pyzmq-25.1.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:a86c2dd76ef71a773e70551a07318b8e52379f58dafa7ae1e0a4be78efd1ff16"}, + {file = "pyzmq-25.1.2-cp36-cp36m-win32.whl", hash = "sha256:359f7f74b5d3c65dae137f33eb2bcfa7ad9ebefd1cab85c935f063f1dbb245cc"}, + {file = "pyzmq-25.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:55875492f820d0eb3417b51d96fea549cde77893ae3790fd25491c5754ea2f68"}, + {file = "pyzmq-25.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b8c8a419dfb02e91b453615c69568442e897aaf77561ee0064d789705ff37a92"}, + {file = "pyzmq-25.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8807c87fa893527ae8a524c15fc505d9950d5e856f03dae5921b5e9aa3b8783b"}, + {file = "pyzmq-25.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5e319ed7d6b8f5fad9b76daa0a68497bc6f129858ad956331a5835785761e003"}, + {file = "pyzmq-25.1.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3c53687dde4d9d473c587ae80cc328e5b102b517447456184b485587ebd18b62"}, + {file = "pyzmq-25.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9add2e5b33d2cd765ad96d5eb734a5e795a0755f7fc49aa04f76d7ddda73fd70"}, + {file = "pyzmq-25.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:e690145a8c0c273c28d3b89d6fb32c45e0d9605b2293c10e650265bf5c11cfec"}, + {file = "pyzmq-25.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:00a06faa7165634f0cac1abb27e54d7a0b3b44eb9994530b8ec73cf52e15353b"}, + {file = "pyzmq-25.1.2-cp37-cp37m-win32.whl", hash = "sha256:0f97bc2f1f13cb16905a5f3e1fbdf100e712d841482b2237484360f8bc4cb3d7"}, + {file = "pyzmq-25.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6cc0020b74b2e410287e5942e1e10886ff81ac77789eb20bec13f7ae681f0fdd"}, + {file = "pyzmq-25.1.2-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:bef02cfcbded83473bdd86dd8d3729cd82b2e569b75844fb4ea08fee3c26ae41"}, + {file = "pyzmq-25.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e10a4b5a4b1192d74853cc71a5e9fd022594573926c2a3a4802020360aa719d8"}, + {file = "pyzmq-25.1.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8c5f80e578427d4695adac6fdf4370c14a2feafdc8cb35549c219b90652536ae"}, + {file = "pyzmq-25.1.2-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5dde6751e857910c1339890f3524de74007958557593b9e7e8c5f01cd919f8a7"}, + {file = "pyzmq-25.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea1608dd169da230a0ad602d5b1ebd39807ac96cae1845c3ceed39af08a5c6df"}, + {file = "pyzmq-25.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0f513130c4c361201da9bc69df25a086487250e16b5571ead521b31ff6b02220"}, + {file = "pyzmq-25.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:019744b99da30330798bb37df33549d59d380c78e516e3bab9c9b84f87a9592f"}, + {file = "pyzmq-25.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2e2713ef44be5d52dd8b8e2023d706bf66cb22072e97fc71b168e01d25192755"}, + {file = "pyzmq-25.1.2-cp38-cp38-win32.whl", hash = "sha256:07cd61a20a535524906595e09344505a9bd46f1da7a07e504b315d41cd42eb07"}, + {file = "pyzmq-25.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb7e49a17fb8c77d3119d41a4523e432eb0c6932187c37deb6fbb00cc3028088"}, + {file = "pyzmq-25.1.2-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:94504ff66f278ab4b7e03e4cba7e7e400cb73bfa9d3d71f58d8972a8dc67e7a6"}, + {file = "pyzmq-25.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6dd0d50bbf9dca1d0bdea219ae6b40f713a3fb477c06ca3714f208fd69e16fd8"}, + {file = "pyzmq-25.1.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:004ff469d21e86f0ef0369717351073e0e577428e514c47c8480770d5e24a565"}, + {file = "pyzmq-25.1.2-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c0b5ca88a8928147b7b1e2dfa09f3b6c256bc1135a1338536cbc9ea13d3b7add"}, + {file = "pyzmq-25.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c9a79f1d2495b167119d02be7448bfba57fad2a4207c4f68abc0bab4b92925b"}, + {file = "pyzmq-25.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:518efd91c3d8ac9f9b4f7dd0e2b7b8bf1a4fe82a308009016b07eaa48681af82"}, + {file = "pyzmq-25.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:1ec23bd7b3a893ae676d0e54ad47d18064e6c5ae1fadc2f195143fb27373f7f6"}, + {file = "pyzmq-25.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db36c27baed588a5a8346b971477b718fdc66cf5b80cbfbd914b4d6d355e44e2"}, + {file = "pyzmq-25.1.2-cp39-cp39-win32.whl", hash = "sha256:39b1067f13aba39d794a24761e385e2eddc26295826530a8c7b6c6c341584289"}, + {file = "pyzmq-25.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:8e9f3fabc445d0ce320ea2c59a75fe3ea591fdbdeebec5db6de530dd4b09412e"}, + {file = "pyzmq-25.1.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a8c1d566344aee826b74e472e16edae0a02e2a044f14f7c24e123002dcff1c05"}, + {file = "pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:759cfd391a0996345ba94b6a5110fca9c557ad4166d86a6e81ea526c376a01e8"}, + {file = "pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c61e346ac34b74028ede1c6b4bcecf649d69b707b3ff9dc0fab453821b04d1e"}, + {file = "pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cb8fc1f8d69b411b8ec0b5f1ffbcaf14c1db95b6bccea21d83610987435f1a4"}, + {file = "pyzmq-25.1.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3c00c9b7d1ca8165c610437ca0c92e7b5607b2f9076f4eb4b095c85d6e680a1d"}, + {file = "pyzmq-25.1.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:df0c7a16ebb94452d2909b9a7b3337940e9a87a824c4fc1c7c36bb4404cb0cde"}, + {file = "pyzmq-25.1.2-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:45999e7f7ed5c390f2e87ece7f6c56bf979fb213550229e711e45ecc7d42ccb8"}, + {file = "pyzmq-25.1.2-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ac170e9e048b40c605358667aca3d94e98f604a18c44bdb4c102e67070f3ac9b"}, + {file = "pyzmq-25.1.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1b604734bec94f05f81b360a272fc824334267426ae9905ff32dc2be433ab96"}, + {file = "pyzmq-25.1.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:a793ac733e3d895d96f865f1806f160696422554e46d30105807fdc9841b9f7d"}, + {file = "pyzmq-25.1.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0806175f2ae5ad4b835ecd87f5f85583316b69f17e97786f7443baaf54b9bb98"}, + {file = "pyzmq-25.1.2-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ef12e259e7bc317c7597d4f6ef59b97b913e162d83b421dd0db3d6410f17a244"}, + {file = "pyzmq-25.1.2-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea253b368eb41116011add00f8d5726762320b1bda892f744c91997b65754d73"}, + {file = "pyzmq-25.1.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b9b1f2ad6498445a941d9a4fee096d387fee436e45cc660e72e768d3d8ee611"}, + {file = "pyzmq-25.1.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:8b14c75979ce932c53b79976a395cb2a8cd3aaf14aef75e8c2cb55a330b9b49d"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:889370d5174a741a62566c003ee8ddba4b04c3f09a97b8000092b7ca83ec9c49"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a18fff090441a40ffda8a7f4f18f03dc56ae73f148f1832e109f9bffa85df15"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99a6b36f95c98839ad98f8c553d8507644c880cf1e0a57fe5e3a3f3969040882"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4345c9a27f4310afbb9c01750e9461ff33d6fb74cd2456b107525bbeebcb5be3"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3516e0b6224cf6e43e341d56da15fd33bdc37fa0c06af4f029f7d7dfceceabbc"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:146b9b1f29ead41255387fb07be56dc29639262c0f7344f570eecdcd8d683314"}, + {file = "pyzmq-25.1.2.tar.gz", hash = "sha256:93f1aa311e8bb912e34f004cf186407a4e90eec4f0ecc0efd26056bf7eda0226"}, ] [package.dependencies] @@ -3603,56 +3613,33 @@ files = [ [[package]] name = "schema-salad" -version = "8.4.20231117150958" +version = "8.5.20240102191335" description = "Schema Annotations for Linked Avro Data (SALAD)" optional = false python-versions = ">=3.8,<3.13" files = [ - {file = "schema-salad-8.4.20231117150958.tar.gz", hash = "sha256:b3e2c2fe00dfb943f8fa15893809bb241875abe0cdefa831cf5df2351dafc245"}, - {file = "schema_salad-8.4.20231117150958-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:7e05633f8b8b86037e19c61c3a102f7a173d24e7959d4ff8fcbf3d9f145d04ec"}, - {file = "schema_salad-8.4.20231117150958-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cccad637d2b20512fe7700e0445904323f44cbbd761e5c06f998c1db708acf7a"}, - {file = "schema_salad-8.4.20231117150958-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fda0e64b32ae7285a0bff8c01b45cf4f592b982cc23288454271edabf279767"}, - {file = "schema_salad-8.4.20231117150958-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:09f67664d38e63470a45208948cc7811804d712c5ab287236437bf6aeac654d5"}, - {file = "schema_salad-8.4.20231117150958-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b0c0ded8ce59d45d9ccea0dc068e86b82eb3ad692f858d8c9edbf4b3bdf2d21f"}, - {file = "schema_salad-8.4.20231117150958-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:017bd1a7a36c4b1d3579afce85a9a4905af6602697f425f2332f8acd0fe91b77"}, - {file = "schema_salad-8.4.20231117150958-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f647e3e6c08c2b60494eef242eba44f17061bc60b55eed94736d86be5923d8e6"}, - {file = "schema_salad-8.4.20231117150958-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fbed0084d8fba0aa10fe68cfe7da7314c6bbe7f68f09477a7dba95d59db075ef"}, - {file = "schema_salad-8.4.20231117150958-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4177098a7d3507c92e30abe4e7434e1043ed7bf114521f710e00ee3e90d4797f"}, - {file = "schema_salad-8.4.20231117150958-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:61e2b338e450cb625824dc3fe81454f6c7ad611f7f3227cbb085a10b1c7ac541"}, - {file = "schema_salad-8.4.20231117150958-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9d67f7ace1ed6d6b9843ef9f217ebc542ae56a800fdfef2c864046a8b65dc9b"}, - {file = "schema_salad-8.4.20231117150958-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ec5cc62e7c1559c8f7a10e0f4b3147b7ea163761bae6c4aa60a092fd3c00f564"}, - {file = "schema_salad-8.4.20231117150958-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1918cce6b19416c7cb3115c73e0220e08976cfa876662c5e4a465e4c9610b5e2"}, - {file = "schema_salad-8.4.20231117150958-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7496533944488fbbb40f55f5e27797e2be6eadb7dd7ce2a09db9d4bc4a6a7322"}, - {file = "schema_salad-8.4.20231117150958-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d2b6875bf9bf7d74d9ffafd0f6210182ec8c06dd1034ad1c45a399ee819f2eb7"}, - {file = "schema_salad-8.4.20231117150958-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fcea35a77f5f9a8a5242039e11be782d8d09dbcd9f131dfeaeba3631f9886017"}, - {file = "schema_salad-8.4.20231117150958-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:885d477ed9886cc47be077388f4bc561aac5b2a1a2de7228343f1680fd26d69d"}, - {file = "schema_salad-8.4.20231117150958-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a51696ed14fcadb4f79941d3c2a622e06653d40fb12db950eb11e68a74b2067a"}, - {file = "schema_salad-8.4.20231117150958-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9f7f270ae5710c75d8478d121a613b259b75d292b739c12769fc80085fa737a7"}, - {file = "schema_salad-8.4.20231117150958-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b1d3ffe49c432b03e8823b812ff2ee43e5e798bcb1a6320d5bfb6a970f84e491"}, - {file = "schema_salad-8.4.20231117150958-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec2158fa67128908f9f303369b20ab389f77dc62b4c1a373599d9bedf3d0b1fe"}, - {file = "schema_salad-8.4.20231117150958-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:14d0175d11357b2f17a1967e6233e549f1025324466b5f066cfe2292ad4b8009"}, - {file = "schema_salad-8.4.20231117150958-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:da6538c02c4b447f940703cacc807e36a2f5bb9bcee729ba2a13b9170e98cda0"}, - {file = "schema_salad-8.4.20231117150958-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:418ca91701c51bb98c730731e5f4fbc4f563cb2e92b3a8d14b30f779115e6513"}, - {file = "schema_salad-8.4.20231117150958-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b913123ab22b2290ca39f0ef79a21f91918f774883032c085942932522696ee6"}, - {file = "schema_salad-8.4.20231117150958-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfe24351663149536f8d2968eb502137f4e4dd774dd3c3288a2d38e13ebf2154"}, - {file = "schema_salad-8.4.20231117150958-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5579c97710b0fefb8022b597041fcf1cdf54bc1dacb2c3dc08b45b4d774b0e5c"}, - {file = "schema_salad-8.4.20231117150958-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:555b6f963f56a0b135fe145754add837f146f6fed5c84f2d38d6fc31dee01cb1"}, - {file = "schema_salad-8.4.20231117150958-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d93be6ea3852749622e643d7aeb7a5906c3be04ffc2acee5dc931eecbf445b20"}, - {file = "schema_salad-8.4.20231117150958-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:fa2ad0708c30370769170f2b18d7f3d0f1fd3743b307f33872ba74faec55cce1"}, - {file = "schema_salad-8.4.20231117150958-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:f0a8d7492f4dc00188448365ef1fd32e782272faa42dd84487f4e4a9a42860ea"}, - {file = "schema_salad-8.4.20231117150958-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc76d4eb047c0715f2a74145f1ef87b50b3902285b2b9061fa00e802ee6418ed"}, - {file = "schema_salad-8.4.20231117150958-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52378b74bc8a3d11437b425fb9e65b87ebd101e6f97422fbe1e852e1cc11695f"}, - {file = "schema_salad-8.4.20231117150958-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e8c9ac0f6bea28c795a179afd8c23574d7f16d588159deed07a9d2af47e6e82"}, - {file = "schema_salad-8.4.20231117150958-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3b7767e5e9d52e8acbe0434c0919da2fe3987078f4796dc86413db69a91e93a7"}, - {file = "schema_salad-8.4.20231117150958-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6472036255fac9d70f428e823d105dd1c2aeaffd184dfe64c8edfa03010ca1ac"}, - {file = "schema_salad-8.4.20231117150958-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:cbfbdfbb4e0fd8827d362112522c9b10a0c30e7842efe3050f13f01d35e5c7f6"}, - {file = "schema_salad-8.4.20231117150958-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:02527cb00f34a46d6aedc767676c851f94a7e1bbd1de33738170d417ef3e2deb"}, - {file = "schema_salad-8.4.20231117150958-py3-none-any.whl", hash = "sha256:3d6f8bf000703305c110ef61cb66bf148d4a8db847a4975c7ab80c86944ec2d3"}, + {file = "schema-salad-8.5.20240102191335.tar.gz", hash = "sha256:7d32704f50e3fc6c096dc162084bf00bebce70aa762cb4a897239a64ae00a20c"}, + {file = "schema_salad-8.5.20240102191335-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:372d35ee19b6cf2ca5b5f367402d7a4f76cf2807303ee0650233360775b1ed5d"}, + {file = "schema_salad-8.5.20240102191335-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3b9be4830970980669bcb461a72e746a3671f0b95693ae52d17212306292b02a"}, + {file = "schema_salad-8.5.20240102191335-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6aa3fecd5f020db85b0fafb6fb29c3b1f083b6f06d0cb647febd1d117ed94e54"}, + {file = "schema_salad-8.5.20240102191335-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a8e9f82a6165ee5533f0f7965a4e799623c5c3d927748fe4595cd6096520bc7"}, + {file = "schema_salad-8.5.20240102191335-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e80ae988772437579e1c0639dc514282e3ecc69fc93d649540f8c5888dd5e39e"}, + {file = "schema_salad-8.5.20240102191335-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9899580a7dcb60360223590754813c0a6cc57af6b335aa84bb3df75cdf978fa8"}, + {file = "schema_salad-8.5.20240102191335-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:93e820ee6788acc55621d5a8d6d037f7d38ab7f6e1020508b054e7a35c13e56e"}, + {file = "schema_salad-8.5.20240102191335-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:aff395737f42246e094d58529fb78e5af42739eaada778188d7fb3fd02fd44de"}, + {file = "schema_salad-8.5.20240102191335-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7c3100113861af20a04557d869a7714b09bb216a5b5f6606750849fb1884e8dc"}, + {file = "schema_salad-8.5.20240102191335-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce76503bc976e419f518e72ca8344258be24c52d708a58a96eb3dd0d34102714"}, + {file = "schema_salad-8.5.20240102191335-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a1d5630972dd832a215924c419942d08234cd7820adff3c2b5b5439190d034f9"}, + {file = "schema_salad-8.5.20240102191335-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9878a1320b81e65d6d74040372eb772878d422343dae240925d959cd24f2490f"}, + {file = "schema_salad-8.5.20240102191335-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:909e4e5e11ee44850d4c445cd461e7610d455b94c81bf7e109515d1ebb5a8e54"}, + {file = "schema_salad-8.5.20240102191335-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a98780163f12b429a1e38c1c9410f5c51cfe7f1b304c2ccb4b8c253488ced876"}, + {file = "schema_salad-8.5.20240102191335-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7111403d51203769177bc8b261f9c19198c49eec239e0f4598f49900c3c7ed99"}, + {file = "schema_salad-8.5.20240102191335-py3-none-any.whl", hash = "sha256:45a29c2710564307249d346babe7ccd7812da1231303a05f39fb837e76d3083c"}, ] [package.dependencies] CacheControl = {version = ">=0.11.7,<0.14", extras = ["filecache"]} -importlib-resources = ">=1.4" +importlib-resources = {version = ">=1.4", markers = "python_version < \"3.9\""} mistune = ">=2.0.3,<2.1" mypy-extensions = "*" rdflib = ">=4.2.2,<8.0.0" @@ -3665,13 +3652,13 @@ pycodegen = ["black"] [[package]] name = "sentry-sdk" -version = "1.38.0" +version = "1.39.2" description = "Python client for Sentry (https://sentry.io)" optional = true python-versions = "*" files = [ - {file = "sentry-sdk-1.38.0.tar.gz", hash = "sha256:8feab81de6bbf64f53279b085bd3820e3e737403b0a0d9317f73a2c3374ae359"}, - {file = "sentry_sdk-1.38.0-py2.py3-none-any.whl", hash = "sha256:0017fa73b8ae2d4e57fd2522ee3df30453715b29d2692142793ec5d5f90b94a6"}, + {file = "sentry-sdk-1.39.2.tar.gz", hash = "sha256:24c83b0b41c887d33328a9166f5950dc37ad58f01c9f2fbff6b87a6f1094170c"}, + {file = "sentry_sdk-1.39.2-py2.py3-none-any.whl", hash = "sha256:acaf597b30258fc7663063b291aa99e58f3096e91fe1e6634f4b79f9c1943e8e"}, ] [package.dependencies] @@ -3700,7 +3687,7 @@ huey = ["huey (>=2)"] loguru = ["loguru (>=0.5)"] opentelemetry = ["opentelemetry-distro (>=0.35b0)"] opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] -pure-eval = ["asttokens", "executing", "pure-eval"] +pure-eval = ["asttokens", "executing", "pure_eval"] pymongo = ["pymongo (>=3.1)"] pyspark = ["pyspark (>=2.4.4)"] quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] @@ -3713,13 +3700,13 @@ tornado = ["tornado (>=5)"] [[package]] name = "setuptools" -version = "69.0.2" +version = "69.0.3" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.0.2-py3-none-any.whl", hash = "sha256:1e8fdff6797d3865f37397be788a4e3cba233608e9b509382a2777d25ebde7f2"}, - {file = "setuptools-69.0.2.tar.gz", hash = "sha256:735896e78a4742605974de002ac60562d286fa8051a7e2299445e8e8fbb01aa6"}, + {file = "setuptools-69.0.3-py3-none-any.whl", hash = "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05"}, + {file = "setuptools-69.0.3.tar.gz", hash = "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78"}, ] [package.extras] @@ -3865,13 +3852,13 @@ dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client", "wheel"] [[package]] name = "sphinx-tabs" -version = "3.4.1" +version = "3.4.4" description = "Tabbed views for Sphinx" optional = false python-versions = "~=3.7" files = [ - {file = "sphinx-tabs-3.4.1.tar.gz", hash = "sha256:d2a09f9e8316e400d57503f6df1c78005fdde220e5af589cc79d493159e1b832"}, - {file = "sphinx_tabs-3.4.1-py3-none-any.whl", hash = "sha256:7cea8942aeccc5d01a995789c01804b787334b55927f29b36ba16ed1e7cb27c6"}, + {file = "sphinx-tabs-3.4.4.tar.gz", hash = "sha256:f1b72c4f23d1ba9cdcaf880fd883524bc70689f561b9785719b8b3c3c5ed0aca"}, + {file = "sphinx_tabs-3.4.4-py3-none-any.whl", hash = "sha256:85939b689a0b0a24bf0da418b9acf14b0b0fca7a7a5cd35461ee452a2d4e716b"}, ] [package.dependencies] @@ -3881,7 +3868,7 @@ sphinx = "*" [package.extras] code-style = ["pre-commit (==2.13.0)"] -testing = ["bs4", "coverage", "pygments", "pytest (>=7.1,<8)", "pytest-cov", "pytest-regressions", "rinohtype", "sphinx-testing"] +testing = ["bs4", "coverage", "pygments", "pytest (>=7.1,<8)", "pytest-cov", "pytest-regressions", "rinohtype"] [[package]] name = "sphinxcontrib-applehelp" @@ -4020,13 +4007,13 @@ widechars = ["wcwidth"] [[package]] name = "termcolor" -version = "2.3.0" +version = "2.4.0" description = "ANSI color formatting for output in terminal" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "termcolor-2.3.0-py3-none-any.whl", hash = "sha256:3afb05607b89aed0ffe25202399ee0867ad4d3cb4180d98aaf8eefa6a5f7d475"}, - {file = "termcolor-2.3.0.tar.gz", hash = "sha256:b5b08f68937f138fe92f6c089b99f1e2da0ae56c52b78bf7075fd95420fd9a5a"}, + {file = "termcolor-2.4.0-py3-none-any.whl", hash = "sha256:9297c0df9c99445c2412e832e882a7884038a25617c60cea2ad69488d4040d63"}, + {file = "termcolor-2.4.0.tar.gz", hash = "sha256:aab9e56047c8ac41ed798fa36d892a37aca6b3e9159f3e0c24bc64a9b3ac7b7a"}, ] [package.extras] @@ -4162,13 +4149,13 @@ testing = ["coverage"] [[package]] name = "types-python-dateutil" -version = "2.8.19.14" +version = "2.8.19.20240106" description = "Typing stubs for python-dateutil" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "types-python-dateutil-2.8.19.14.tar.gz", hash = "sha256:1f4f10ac98bb8b16ade9dbee3518d9ace017821d94b057a425b069f834737f4b"}, - {file = "types_python_dateutil-2.8.19.14-py3-none-any.whl", hash = "sha256:f977b8de27787639986b4e28963263fd0e5158942b3ecef91b9335c130cb1ce9"}, + {file = "types-python-dateutil-2.8.19.20240106.tar.gz", hash = "sha256:1f8db221c3b98e6ca02ea83a58371b22c374f42ae5bbdf186db9c9a76581459f"}, + {file = "types_python_dateutil-2.8.19.20240106-py3-none-any.whl", hash = "sha256:efbbdc54590d0f16152fa103c9879c7d4a00e82078f6e2cf01769042165acaa2"}, ] [[package]] @@ -4209,13 +4196,13 @@ types-urllib3 = "*" [[package]] name = "types-tabulate" -version = "0.9.0.3" +version = "0.9.0.20240106" description = "Typing stubs for tabulate" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "types-tabulate-0.9.0.3.tar.gz", hash = "sha256:197651f9d6467193cd166d8500116a6d3a26f2a4eb2db093bc9535ee1c0be55e"}, - {file = "types_tabulate-0.9.0.3-py3-none-any.whl", hash = "sha256:462d1b62e01728416e8277614d6a3eb172d53a8efaf04a04a973ff2dd45238f6"}, + {file = "types-tabulate-0.9.0.20240106.tar.gz", hash = "sha256:c9b6db10dd7fcf55bd1712dd3537f86ddce72a08fd62bb1af4338c7096ce947e"}, + {file = "types_tabulate-0.9.0.20240106-py3-none-any.whl", hash = "sha256:0378b7b6fe0ccb4986299496d027a6d4c218298ecad67199bbd0e2d7e9d335a1"}, ] [[package]] @@ -4231,13 +4218,13 @@ files = [ [[package]] name = "typing-extensions" -version = "4.8.0" +version = "4.9.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, - {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, + {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, + {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, ] [[package]] @@ -4275,13 +4262,13 @@ yarl = "*" [[package]] name = "virtualenv" -version = "20.24.7" +version = "20.25.0" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.24.7-py3-none-any.whl", hash = "sha256:a18b3fd0314ca59a2e9f4b556819ed07183b3e9a3702ecfe213f593d44f7b3fd"}, - {file = "virtualenv-20.24.7.tar.gz", hash = "sha256:69050ffb42419c91f6c1284a7b24e0475d793447e35929b488bf6a0aade39353"}, + {file = "virtualenv-20.25.0-py3-none-any.whl", hash = "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3"}, + {file = "virtualenv-20.25.0.tar.gz", hash = "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b"}, ] [package.dependencies] @@ -4308,13 +4295,13 @@ redis = ">=3.0.0" [[package]] name = "wcwidth" -version = "0.2.12" +version = "0.2.13" description = "Measures the displayed width of unicode strings in a terminal" optional = false python-versions = "*" files = [ - {file = "wcwidth-0.2.12-py2.py3-none-any.whl", hash = "sha256:f26ec43d96c8cbfed76a5075dac87680124fa84e0855195a6184da9c187f133c"}, - {file = "wcwidth-0.2.12.tar.gz", hash = "sha256:f01c104efdf57971bcb756f054dd58ddec5204dd15fa31d6503ea57947d97c02"}, + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, ] [[package]] @@ -4330,13 +4317,13 @@ files = [ [[package]] name = "websocket-client" -version = "1.6.4" +version = "1.7.0" description = "WebSocket client for Python with low level API options" optional = false python-versions = ">=3.8" files = [ - {file = "websocket-client-1.6.4.tar.gz", hash = "sha256:b3324019b3c28572086c4a319f91d1dcd44e6e11cd340232978c684a7650d0df"}, - {file = "websocket_client-1.6.4-py3-none-any.whl", hash = "sha256:084072e0a7f5f347ef2ac3d8698a5e0b4ffbfcab607628cadabc650fc9a83a24"}, + {file = "websocket-client-1.7.0.tar.gz", hash = "sha256:10e511ea3a8c744631d3bd77e61eb17ed09304c413ad42cf6ddfa4c7787e8fe6"}, + {file = "websocket_client-1.7.0-py3-none-any.whl", hash = "sha256:f4c3d22fec12a2461427a29957ff07d35098ee2d976d3ba244e688b8b4057588"}, ] [package.extras] @@ -4456,101 +4443,101 @@ pyparsing = "*" [[package]] name = "yarl" -version = "1.9.3" +version = "1.9.4" description = "Yet another URL library" optional = false python-versions = ">=3.7" files = [ - {file = "yarl-1.9.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:32435d134414e01d937cd9d6cc56e8413a8d4741dea36af5840c7750f04d16ab"}, - {file = "yarl-1.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9a5211de242754b5e612557bca701f39f8b1a9408dff73c6db623f22d20f470e"}, - {file = "yarl-1.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:525cd69eff44833b01f8ef39aa33a9cc53a99ff7f9d76a6ef6a9fb758f54d0ff"}, - {file = "yarl-1.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc94441bcf9cb8c59f51f23193316afefbf3ff858460cb47b5758bf66a14d130"}, - {file = "yarl-1.9.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e36021db54b8a0475805acc1d6c4bca5d9f52c3825ad29ae2d398a9d530ddb88"}, - {file = "yarl-1.9.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0f17d1df951336a02afc8270c03c0c6e60d1f9996fcbd43a4ce6be81de0bd9d"}, - {file = "yarl-1.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5f3faeb8100a43adf3e7925d556801d14b5816a0ac9e75e22948e787feec642"}, - {file = "yarl-1.9.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aed37db837ecb5962469fad448aaae0f0ee94ffce2062cf2eb9aed13328b5196"}, - {file = "yarl-1.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:721ee3fc292f0d069a04016ef2c3a25595d48c5b8ddc6029be46f6158d129c92"}, - {file = "yarl-1.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b8bc5b87a65a4e64bc83385c05145ea901b613d0d3a434d434b55511b6ab0067"}, - {file = "yarl-1.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:dd952b9c64f3b21aedd09b8fe958e4931864dba69926d8a90c90d36ac4e28c9a"}, - {file = "yarl-1.9.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:c405d482c320a88ab53dcbd98d6d6f32ada074f2d965d6e9bf2d823158fa97de"}, - {file = "yarl-1.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9df9a0d4c5624790a0dea2e02e3b1b3c69aed14bcb8650e19606d9df3719e87d"}, - {file = "yarl-1.9.3-cp310-cp310-win32.whl", hash = "sha256:d34c4f80956227f2686ddea5b3585e109c2733e2d4ef12eb1b8b4e84f09a2ab6"}, - {file = "yarl-1.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:cf7a4e8de7f1092829caef66fd90eaf3710bc5efd322a816d5677b7664893c93"}, - {file = "yarl-1.9.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d61a0ca95503867d4d627517bcfdc28a8468c3f1b0b06c626f30dd759d3999fd"}, - {file = "yarl-1.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:73cc83f918b69110813a7d95024266072d987b903a623ecae673d1e71579d566"}, - {file = "yarl-1.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d81657b23e0edb84b37167e98aefb04ae16cbc5352770057893bd222cdc6e45f"}, - {file = "yarl-1.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26a1a8443091c7fbc17b84a0d9f38de34b8423b459fb853e6c8cdfab0eacf613"}, - {file = "yarl-1.9.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fe34befb8c765b8ce562f0200afda3578f8abb159c76de3ab354c80b72244c41"}, - {file = "yarl-1.9.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c757f64afe53a422e45e3e399e1e3cf82b7a2f244796ce80d8ca53e16a49b9f"}, - {file = "yarl-1.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72a57b41a0920b9a220125081c1e191b88a4cdec13bf9d0649e382a822705c65"}, - {file = "yarl-1.9.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:632c7aeb99df718765adf58eacb9acb9cbc555e075da849c1378ef4d18bf536a"}, - {file = "yarl-1.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b0b8c06afcf2bac5a50b37f64efbde978b7f9dc88842ce9729c020dc71fae4ce"}, - {file = "yarl-1.9.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1d93461e2cf76c4796355494f15ffcb50a3c198cc2d601ad8d6a96219a10c363"}, - {file = "yarl-1.9.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:4003f380dac50328c85e85416aca6985536812c082387255c35292cb4b41707e"}, - {file = "yarl-1.9.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4d6d74a97e898c1c2df80339aa423234ad9ea2052f66366cef1e80448798c13d"}, - {file = "yarl-1.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b61e64b06c3640feab73fa4ff9cb64bd8182de52e5dc13038e01cfe674ebc321"}, - {file = "yarl-1.9.3-cp311-cp311-win32.whl", hash = "sha256:29beac86f33d6c7ab1d79bd0213aa7aed2d2f555386856bb3056d5fdd9dab279"}, - {file = "yarl-1.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:f7271d6bd8838c49ba8ae647fc06469137e1c161a7ef97d778b72904d9b68696"}, - {file = "yarl-1.9.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:dd318e6b75ca80bff0b22b302f83a8ee41c62b8ac662ddb49f67ec97e799885d"}, - {file = "yarl-1.9.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c4b1efb11a8acd13246ffb0bee888dd0e8eb057f8bf30112e3e21e421eb82d4a"}, - {file = "yarl-1.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c6f034386e5550b5dc8ded90b5e2ff7db21f0f5c7de37b6efc5dac046eb19c10"}, - {file = "yarl-1.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd49a908cb6d387fc26acee8b7d9fcc9bbf8e1aca890c0b2fdfd706057546080"}, - {file = "yarl-1.9.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa4643635f26052401750bd54db911b6342eb1a9ac3e74f0f8b58a25d61dfe41"}, - {file = "yarl-1.9.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e741bd48e6a417bdfbae02e088f60018286d6c141639359fb8df017a3b69415a"}, - {file = "yarl-1.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c86d0d0919952d05df880a1889a4f0aeb6868e98961c090e335671dea5c0361"}, - {file = "yarl-1.9.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d5434b34100b504aabae75f0622ebb85defffe7b64ad8f52b8b30ec6ef6e4b9"}, - {file = "yarl-1.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79e1df60f7c2b148722fb6cafebffe1acd95fd8b5fd77795f56247edaf326752"}, - {file = "yarl-1.9.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:44e91a669c43f03964f672c5a234ae0d7a4d49c9b85d1baa93dec28afa28ffbd"}, - {file = "yarl-1.9.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:3cfa4dbe17b2e6fca1414e9c3bcc216f6930cb18ea7646e7d0d52792ac196808"}, - {file = "yarl-1.9.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:88d2c3cc4b2f46d1ba73d81c51ec0e486f59cc51165ea4f789677f91a303a9a7"}, - {file = "yarl-1.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cccdc02e46d2bd7cb5f38f8cc3d9db0d24951abd082b2f242c9e9f59c0ab2af3"}, - {file = "yarl-1.9.3-cp312-cp312-win32.whl", hash = "sha256:96758e56dceb8a70f8a5cff1e452daaeff07d1cc9f11e9b0c951330f0a2396a7"}, - {file = "yarl-1.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:c4472fe53ebf541113e533971bd8c32728debc4c6d8cc177f2bff31d011ec17e"}, - {file = "yarl-1.9.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:126638ab961633f0940a06e1c9d59919003ef212a15869708dcb7305f91a6732"}, - {file = "yarl-1.9.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c99ddaddb2fbe04953b84d1651149a0d85214780e4d0ee824e610ab549d98d92"}, - {file = "yarl-1.9.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dab30b21bd6fb17c3f4684868c7e6a9e8468078db00f599fb1c14e324b10fca"}, - {file = "yarl-1.9.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:828235a2a169160ee73a2fcfb8a000709edf09d7511fccf203465c3d5acc59e4"}, - {file = "yarl-1.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc391e3941045fd0987c77484b2799adffd08e4b6735c4ee5f054366a2e1551d"}, - {file = "yarl-1.9.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51382c72dd5377861b573bd55dcf680df54cea84147c8648b15ac507fbef984d"}, - {file = "yarl-1.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:28a108cb92ce6cf867690a962372996ca332d8cda0210c5ad487fe996e76b8bb"}, - {file = "yarl-1.9.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8f18a7832ff85dfcd77871fe677b169b1bc60c021978c90c3bb14f727596e0ae"}, - {file = "yarl-1.9.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:7eaf13af79950142ab2bbb8362f8d8d935be9aaf8df1df89c86c3231e4ff238a"}, - {file = "yarl-1.9.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:66a6dbf6ca7d2db03cc61cafe1ee6be838ce0fbc97781881a22a58a7c5efef42"}, - {file = "yarl-1.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1a0a4f3aaa18580038cfa52a7183c8ffbbe7d727fe581300817efc1e96d1b0e9"}, - {file = "yarl-1.9.3-cp37-cp37m-win32.whl", hash = "sha256:946db4511b2d815979d733ac6a961f47e20a29c297be0d55b6d4b77ee4b298f6"}, - {file = "yarl-1.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:2dad8166d41ebd1f76ce107cf6a31e39801aee3844a54a90af23278b072f1ccf"}, - {file = "yarl-1.9.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bb72d2a94481e7dc7a0c522673db288f31849800d6ce2435317376a345728225"}, - {file = "yarl-1.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9a172c3d5447b7da1680a1a2d6ecdf6f87a319d21d52729f45ec938a7006d5d8"}, - {file = "yarl-1.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2dc72e891672343b99db6d497024bf8b985537ad6c393359dc5227ef653b2f17"}, - {file = "yarl-1.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8d51817cf4b8d545963ec65ff06c1b92e5765aa98831678d0e2240b6e9fd281"}, - {file = "yarl-1.9.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:53ec65f7eee8655bebb1f6f1607760d123c3c115a324b443df4f916383482a67"}, - {file = "yarl-1.9.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cfd77e8e5cafba3fb584e0f4b935a59216f352b73d4987be3af51f43a862c403"}, - {file = "yarl-1.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e73db54c967eb75037c178a54445c5a4e7461b5203b27c45ef656a81787c0c1b"}, - {file = "yarl-1.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09c19e5f4404574fcfb736efecf75844ffe8610606f3fccc35a1515b8b6712c4"}, - {file = "yarl-1.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6280353940f7e5e2efaaabd686193e61351e966cc02f401761c4d87f48c89ea4"}, - {file = "yarl-1.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c25ec06e4241e162f5d1f57c370f4078797ade95c9208bd0c60f484834f09c96"}, - {file = "yarl-1.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:7217234b10c64b52cc39a8d82550342ae2e45be34f5bff02b890b8c452eb48d7"}, - {file = "yarl-1.9.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4ce77d289f8d40905c054b63f29851ecbfd026ef4ba5c371a158cfe6f623663e"}, - {file = "yarl-1.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5f74b015c99a5eac5ae589de27a1201418a5d9d460e89ccb3366015c6153e60a"}, - {file = "yarl-1.9.3-cp38-cp38-win32.whl", hash = "sha256:8a2538806be846ea25e90c28786136932ec385c7ff3bc1148e45125984783dc6"}, - {file = "yarl-1.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:6465d36381af057d0fab4e0f24ef0e80ba61f03fe43e6eeccbe0056e74aadc70"}, - {file = "yarl-1.9.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2f3c8822bc8fb4a347a192dd6a28a25d7f0ea3262e826d7d4ef9cc99cd06d07e"}, - {file = "yarl-1.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7831566595fe88ba17ea80e4b61c0eb599f84c85acaa14bf04dd90319a45b90"}, - {file = "yarl-1.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ff34cb09a332832d1cf38acd0f604c068665192c6107a439a92abfd8acf90fe2"}, - {file = "yarl-1.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe8080b4f25dfc44a86bedd14bc4f9d469dfc6456e6f3c5d9077e81a5fedfba7"}, - {file = "yarl-1.9.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8535e111a064f3bdd94c0ed443105934d6f005adad68dd13ce50a488a0ad1bf3"}, - {file = "yarl-1.9.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d155a092bf0ebf4a9f6f3b7a650dc5d9a5bbb585ef83a52ed36ba46f55cc39d"}, - {file = "yarl-1.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:778df71c8d0c8c9f1b378624b26431ca80041660d7be7c3f724b2c7a6e65d0d6"}, - {file = "yarl-1.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9f9cafaf031c34d95c1528c16b2fa07b710e6056b3c4e2e34e9317072da5d1a"}, - {file = "yarl-1.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ca6b66f69e30f6e180d52f14d91ac854b8119553b524e0e28d5291a724f0f423"}, - {file = "yarl-1.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e0e7e83f31e23c5d00ff618045ddc5e916f9e613d33c5a5823bc0b0a0feb522f"}, - {file = "yarl-1.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:af52725c7c39b0ee655befbbab5b9a1b209e01bb39128dce0db226a10014aacc"}, - {file = "yarl-1.9.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0ab5baaea8450f4a3e241ef17e3d129b2143e38a685036b075976b9c415ea3eb"}, - {file = "yarl-1.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6d350388ba1129bc867c6af1cd17da2b197dff0d2801036d2d7d83c2d771a682"}, - {file = "yarl-1.9.3-cp39-cp39-win32.whl", hash = "sha256:e2a16ef5fa2382af83bef4a18c1b3bcb4284c4732906aa69422cf09df9c59f1f"}, - {file = "yarl-1.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:d92d897cb4b4bf915fbeb5e604c7911021a8456f0964f3b8ebbe7f9188b9eabb"}, - {file = "yarl-1.9.3-py3-none-any.whl", hash = "sha256:271d63396460b6607b588555ea27a1a02b717ca2e3f2cf53bdde4013d7790929"}, - {file = "yarl-1.9.3.tar.gz", hash = "sha256:4a14907b597ec55740f63e52d7fee0e9ee09d5b9d57a4f399a7423268e457b57"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"}, + {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"}, + {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, + {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, + {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, + {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, + {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, + {file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"}, + {file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"}, + {file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"}, + {file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"}, + {file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"}, + {file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"}, + {file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"}, + {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, + {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, ] [package.dependencies] @@ -4852,4 +4839,4 @@ service = ["apispec", "apispec-oneofschema", "apispec-webframeworks", "circus", [metadata] lock-version = "2.0" python-versions = ">=3.8.1,<3.12" -content-hash = "0cee6806162b0b91841e14824a590123f0ff3f2bc0b38e41d5e27448c09208ed" +content-hash = "cba1daf715b4c0a2d52b4ad20a4f21913d07d0bce8e8357080082797fe1b4b80" diff --git a/pyproject.toml b/pyproject.toml index 6392863199..8dfdb6d2eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,7 +64,7 @@ deal = "~4.24.3" deepdiff = "~6.7.1" deepmerge = "~1.1.0" docker = "^5.0.3" -gitpython = "~3.1.40" +gitpython = "~3.1.41" grandalf = "^0.8" humanize = "~4.9.0" importlib-resources = "~5.12.0" @@ -112,7 +112,7 @@ pillow = { version = "^10.1.0", optional = true } python-dotenv = { version = "^0.20", optional = true } redis = { version = "~5.0.1", optional = true } rq = { version = "~1.15.1", optional = true } -sentry-sdk = { version = "~1.38.0", extras = ["flask"], optional = true } +sentry-sdk = { version = "~1.39.0", extras = ["flask"], optional = true } walrus = { version = "^0.9", optional = true } prometheus-flask-exporter = "^0.23" filetype = "^1.2.0" @@ -163,7 +163,7 @@ plantweb = ">=1.2.1,<1.3.0" renku-sphinx-theme = "^0.4" sphinx-click = "^4.3.0" sphinx-rtd-theme = "~1.3" -sphinx-tabs = "==3.4.1" +sphinx-tabs = "==3.4.4" sphinxcontrib-spelling = ">=7,<9" [tool.poetry.extras] @@ -347,5 +347,5 @@ exclude = ["docs"] [build-system] -requires = ["poetry-core>=1.3.0,<1.7.0", "poetry-dynamic-versioning==0.21.5", "gitpython==3.1.24"] +requires = ["poetry-core>=1.3.0,<1.7.0", "poetry-dynamic-versioning==0.21.5", "gitpython==3.1.41"] build-backend = "poetry_dynamic_versioning.backend" From a7f4a224a1cc2108c3b091b751187bf03ebb83e4 Mon Sep 17 00:00:00 2001 From: "M. Alisafaee" Date: Fri, 12 Jan 2024 23:00:05 +0100 Subject: [PATCH 09/15] feat(service): date_published in datasets.list response (#3648) --- renku/domain_model/dataset.py | 1 + tests/service/views/test_dataset_views.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/renku/domain_model/dataset.py b/renku/domain_model/dataset.py index 005cc58cba..36626f1f24 100644 --- a/renku/domain_model/dataset.py +++ b/renku/domain_model/dataset.py @@ -738,6 +738,7 @@ class DatasetDetailsJson(marshmallow.Schema): slug = marshmallow.fields.String(required=True) version = marshmallow.fields.String(allow_none=True) created_at = marshmallow.fields.String(allow_none=True, attribute="date_created") + date_published = marshmallow.fields.String(allow_none=True) name = marshmallow.fields.String() creators = marshmallow.fields.List(marshmallow.fields.Nested(DatasetCreatorsJson)) diff --git a/tests/service/views/test_dataset_views.py b/tests/service/views/test_dataset_views.py index 1d1e302169..943da0c73e 100644 --- a/tests/service/views/test_dataset_views.py +++ b/tests/service/views/test_dataset_views.py @@ -630,6 +630,7 @@ def test_list_datasets_view(svc_client_with_repo): "annotations", "storage", "data_directory", + "date_published", } == set(response.json["result"]["datasets"][0].keys()) @@ -678,6 +679,7 @@ def test_list_datasets_view_remote(svc_client_with_repo, it_remote_repo_url): "identifier", "images", "created_at", + "date_published", "slug", "name", "creators", @@ -797,6 +799,7 @@ def test_create_and_list_datasets_view(svc_client_with_repo): "annotations", "storage", "data_directory", + "date_published", } == set(response.json["result"]["datasets"][0].keys()) assert payload["slug"] in [ds["slug"] for ds in response.json["result"]["datasets"]] From 0c523facd2647e04285430b4a5fc64c912df1b1f Mon Sep 17 00:00:00 2001 From: Ralf Grubenmann Date: Mon, 15 Jan 2024 17:26:10 +0100 Subject: [PATCH 10/15] fix(service): fix clone depth not being respected (#3678) --- renku/ui/service/cache/projects.py | 4 ++-- renku/ui/service/cache/serializers/project.py | 2 +- renku/ui/service/config.py | 2 +- renku/ui/service/controllers/templates_create_project.py | 4 ---- renku/ui/service/gateways/repository_cache.py | 2 +- tests/service/cache/test_cache.py | 5 +---- tests/service/fixtures/service_projects.py | 3 --- tests/service/jobs/test_jobs.py | 3 --- tests/service/views/test_dataset_views.py | 3 --- 9 files changed, 6 insertions(+), 22 deletions(-) diff --git a/renku/ui/service/cache/projects.py b/renku/ui/service/cache/projects.py index e95d2bce88..2cb06085bf 100644 --- a/renku/ui/service/cache/projects.py +++ b/renku/ui/service/cache/projects.py @@ -16,7 +16,7 @@ """Renku service project cache management.""" from typing import cast -from marshmallow import EXCLUDE +from marshmallow import RAISE from renku.ui.service.cache.base import BaseCache from renku.ui.service.cache.models.project import Project @@ -34,7 +34,7 @@ def make_project(self, user, project_data, persist=True) -> Project: """Store user project metadata.""" project_data.update({"user_id": user.user_id}) - project_obj: Project = cast(Project, self.project_schema.load(project_data, unknown=EXCLUDE)) + project_obj: Project = cast(Project, self.project_schema.load(project_data, unknown=RAISE)) if persist: project_obj.save() diff --git a/renku/ui/service/cache/serializers/project.py b/renku/ui/service/cache/serializers/project.py index d9e484ebc6..e186e66d7a 100644 --- a/renku/ui/service/cache/serializers/project.py +++ b/renku/ui/service/cache/serializers/project.py @@ -31,7 +31,7 @@ class ProjectSchema(CreationSchema, AccessSchema, MandatoryUserSchema): project_id = fields.String(load_default=lambda: uuid.uuid4().hex) - clone_depth = fields.Integer() + clone_depth = fields.Integer(allow_none=True) git_url = fields.String() name = fields.String(required=True) diff --git a/renku/ui/service/config.py b/renku/ui/service/config.py index d66d8a4236..e7085d44e0 100644 --- a/renku/ui/service/config.py +++ b/renku/ui/service/config.py @@ -36,7 +36,7 @@ OPENAPI_VERSION = "3.0.3" API_VERSION = "v1" -PROJECT_CLONE_NO_DEPTH = -1 +PROJECT_CLONE_NO_DEPTH = None PROJECT_CLONE_DEPTH_DEFAULT = int(os.getenv("PROJECT_CLONE_DEPTH_DEFAULT", 1)) TEMPLATE_CLONE_DEPTH_DEFAULT = int(os.getenv("TEMPLATE_CLONE_DEPTH_DEFAULT", 0)) diff --git a/renku/ui/service/controllers/templates_create_project.py b/renku/ui/service/controllers/templates_create_project.py index 0b988f47d1..74048896f8 100644 --- a/renku/ui/service/controllers/templates_create_project.py +++ b/renku/ui/service/controllers/templates_create_project.py @@ -98,12 +98,8 @@ def setup_new_project(self): "name": self.ctx["project_name"], "slug": self.ctx["project_name_stripped"], "description": self.ctx["project_description"], - "fullname": self.ctx["fullname"], - "email": self.ctx["email"], "owner": self.ctx["project_namespace"], - "token": self.ctx["token"], "initialized": True, - "image": self.ctx["image"], } project = self.cache.make_project(self.user, new_project_data) diff --git a/renku/ui/service/gateways/repository_cache.py b/renku/ui/service/gateways/repository_cache.py index 158d1d9f98..a163f81a39 100644 --- a/renku/ui/service/gateways/repository_cache.py +++ b/renku/ui/service/gateways/repository_cache.py @@ -142,7 +142,7 @@ def _clone_project( "owner": parsed_git_url.owner, "name": parsed_git_url.name, "slug": normalize_to_ascii(parsed_git_url.name), - "depth": PROJECT_CLONE_DEPTH_DEFAULT if shallow else None, + "clone_depth": PROJECT_CLONE_DEPTH_DEFAULT if shallow else None, "branch": branch, "git_url": git_url, "user_id": user.user_id, diff --git a/tests/service/cache/test_cache.py b/tests/service/cache/test_cache.py index 34bb84b455..6c75c6e083 100644 --- a/tests/service/cache/test_cache.py +++ b/tests/service/cache/test_cache.py @@ -253,11 +253,8 @@ def test_service_cache_make_project(svc_client_cache): project_data = { "name": "renku-project-template", "slug": "renku-project-template.git", - "depth": 1, + "clone_depth": 1, "git_url": "https://github.com/SwissDataScienceCenter/renku-project-template.git", - "email": "contact@renkulab.io", - "fullname": "renku the frog", - "token": "None", "owner": "SwissDataScienceCenter", } project = cache.make_project(user, project_data) diff --git a/tests/service/fixtures/service_projects.py b/tests/service/fixtures/service_projects.py index 95811242ef..8109e52bed 100644 --- a/tests/service/fixtures/service_projects.py +++ b/tests/service/fixtures/service_projects.py @@ -36,10 +36,7 @@ def project_metadata(project) -> Generator[Tuple["RenkuProject", Dict[str, Any]] "project_id": uuid.uuid4().hex, "name": name, "slug": normalize_to_ascii(name), - "fullname": "full project name", - "email": "my@email.com", "owner": "me", - "token": "awesome token", "git_url": "https://example.com/a/b.git", "initialized": True, "branch": "", diff --git a/tests/service/jobs/test_jobs.py b/tests/service/jobs/test_jobs.py index b3618e1fac..f19c46ff52 100644 --- a/tests/service/jobs/test_jobs.py +++ b/tests/service/jobs/test_jobs.py @@ -108,10 +108,7 @@ def test_job_constructor_lock(svc_client_with_user, service_job): "project_id": uuid.uuid4().hex, "name": "my-project", "slug": "my-project", - "fullname": "full project name", - "email": "my@email.com", "owner": "me", - "token": "awesome token", "git_url": "git@gitlab.com", "initialized": True, } diff --git a/tests/service/views/test_dataset_views.py b/tests/service/views/test_dataset_views.py index 943da0c73e..5dbfc5a71c 100644 --- a/tests/service/views/test_dataset_views.py +++ b/tests/service/views/test_dataset_views.py @@ -1012,10 +1012,7 @@ def test_cached_import_dataset_job(doi, svc_client_cache, project): "project_id": uuid.uuid4().hex, "name": name, "slug": normalize_to_ascii(name), - "fullname": "full project name", - "email": "my@email.com", "owner": "me", - "token": "awesome token", "git_url": "https://example.com/a/b.git", "initialized": True, } From 00da76851e5c263c89d168b0cd6e57d71244c446 Mon Sep 17 00:00:00 2001 From: "M. Alisafaee" Date: Wed, 17 Jan 2024 09:32:44 +0100 Subject: [PATCH 11/15] fix(service): accept commit-sha in config.show (#3685) --- renku/ui/service/controllers/api/mixins.py | 12 +-- .../controllers/utils/remote_project.py | 12 ++- renku/ui/service/serializers/cache.py | 1 + tests/service/fixtures/service_integration.py | 3 + tests/service/views/test_config_views.py | 78 ++++++++++++++++++- 5 files changed, 96 insertions(+), 10 deletions(-) diff --git a/renku/ui/service/controllers/api/mixins.py b/renku/ui/service/controllers/api/mixins.py index 3f0bf43317..d1d62a61c1 100644 --- a/renku/ui/service/controllers/api/mixins.py +++ b/renku/ui/service/controllers/api/mixins.py @@ -183,12 +183,12 @@ def local(self): raise ProgramRenkuError(error) project = LocalRepositoryCache().get( - self.cache, - self.request_data["git_url"], - self.request_data.get("branch"), - self.user, - self.clone_depth is not None, - self.request_data.get("commit_sha"), + cache=self.cache, + git_url=self.request_data["git_url"], + branch=self.request_data.get("branch"), + user=self.user, + shallow=self.clone_depth is not None, + commit_sha=self.request_data.get("commit_sha"), ) self.context["project_id"] = project.project_id diff --git a/renku/ui/service/controllers/utils/remote_project.py b/renku/ui/service/controllers/utils/remote_project.py index ae22f3c053..f3c4e404fa 100644 --- a/renku/ui/service/controllers/utils/remote_project.py +++ b/renku/ui/service/controllers/utils/remote_project.py @@ -23,7 +23,7 @@ from renku.core import errors from renku.core.util.contexts import renku_project_context -from renku.infrastructure.repository import Repository +from renku.core.util.git import clone_renku_repository from renku.ui.service.serializers.cache import ProjectCloneContext from renku.ui.service.utils import normalize_git_url @@ -46,6 +46,7 @@ def __init__(self, user_data, request_data): self.git_url = normalize_git_url(self.ctx["url_with_auth"]) self.branch = self.ctx["branch"] + self.commit_sha = self.ctx["commit_sha"] or "" @property def remote_url(self): @@ -65,7 +66,14 @@ def remote(self): """Retrieve project metadata.""" with tempfile.TemporaryDirectory() as td: try: - Repository.clone_from(self.remote_url.geturl(), td, branch=self.branch, depth=1) + clone_renku_repository( + url=self.remote_url.geturl(), + path=td, + install_lfs=False, + depth=None if self.branch or self.commit_sha else 1, + checkout_revision=self.commit_sha or self.branch, + raise_git_except=True, + ) except errors.GitCommandError as e: msg = str(e) if "is not a commit and a branch" in msg and "cannot be created from it" in msg: diff --git a/renku/ui/service/serializers/cache.py b/renku/ui/service/serializers/cache.py index 81cdb64fd9..17db739248 100644 --- a/renku/ui/service/serializers/cache.py +++ b/renku/ui/service/serializers/cache.py @@ -132,6 +132,7 @@ class RepositoryCloneRequest(RemoteRepositorySchema): """Request schema for repository clone.""" depth = fields.Integer(metadata={"description": "Git fetch depth"}, load_default=PROJECT_CLONE_DEPTH_DEFAULT) + commit_sha = fields.String(required=False, load_default=None, dump_default=None) class ProjectCloneContext(RepositoryCloneRequest): diff --git a/tests/service/fixtures/service_integration.py b/tests/service/fixtures/service_integration.py index d75bb7f11f..33bcab7edd 100644 --- a/tests/service/fixtures/service_integration.py +++ b/tests/service/fixtures/service_integration.py @@ -97,6 +97,7 @@ def integration_lifecycle( it_protected_repo_url, it_workflow_repo_url, it_remote_old_repo_url, + it_remote_public_repo_url, request, ): """Setup and teardown steps for integration tests.""" @@ -115,6 +116,8 @@ def integration_lifecycle( remote_repo = it_workflow_repo_url elif marker.args[0] == "old": remote_repo = it_remote_old_repo_url + elif marker.args[0] == "public": + remote_repo = it_remote_public_repo_url else: raise ValueError(f"Couldn't get remote repo for marker {marker.args[0]}") diff --git a/tests/service/views/test_config_views.py b/tests/service/views/test_config_views.py index f28324d4dc..5245d1a667 100644 --- a/tests/service/views/test_config_views.py +++ b/tests/service/views/test_config_views.py @@ -15,7 +15,9 @@ # limitations under the License. """Renku service config view tests.""" +import configparser import json +import uuid import pytest @@ -43,6 +45,78 @@ def test_config_view_show(svc_client_with_repo): assert 200 == response.status_code +@pytest.mark.service +@pytest.mark.integration +@retry_failed +@pytest.mark.remote_repo("public") +@pytest.mark.parametrize("anonymous", [False, True]) +def test_config_view_show_with_branch(svc_client_setup, anonymous): + """Check config show view in a different branch.""" + svc_client, headers, project_id, url_components, repository = svc_client_setup + + if anonymous: + headers = {} + + config_filepath = repository.path / ".renku" / "renku.ini" + current_branch = repository.active_branch.name + new_branch = uuid.uuid4().hex + + # Write a default config value + config = configparser.ConfigParser() + config.add_section("interactive") + config["interactive"]["default_url"] = "/lab" + config.add_section("renku") + config["renku"]["test-config"] = "current-branch" + with open(config_filepath, "w") as f: + config.write(f) + + repository.add(all=True) + repository.commit("master config") + repository.push(remote="origin", refspec=current_branch) + current_commit_sha = repository.active_branch.commit.hexsha + + # Create a new branch and a modified config + repository.branches.add(new_branch) + repository.checkout(new_branch) + config["renku"]["test-config"] = "new-branch" + with open(config_filepath, "w") as f: + config.write(f) + + repository.add(all=True) + repository.commit("new config") + repository.push(remote="origin", refspec=new_branch) + + params = { + "git_url": url_components.href, + "branch": current_branch, + } + + response = svc_client.get("/config.show", query_string=params, headers=headers) + + assert 200 == response.status_code + assert "current-branch" == response.json["result"]["config"].get("renku.test-config") + + params = { + "git_url": url_components.href, + "branch": new_branch, + } + + response = svc_client.get("/config.show", query_string=params, headers=headers) + + assert 200 == response.status_code + assert "new-branch" == response.json["result"]["config"].get("renku.test-config") + + params = { + "git_url": url_components.href, + "branch": current_commit_sha, + } + + response = svc_client.get("/config.show", query_string=params, headers=headers) + + assert 200 == response.status_code + assert "current-branch" == response.json["result"]["config"].get("renku.test-config") + + @pytest.mark.service @pytest.mark.integration @retry_failed @@ -133,7 +207,7 @@ def test_config_view_set(svc_client_with_repo): @pytest.mark.service @pytest.mark.integration @retry_failed -def test_config_view_set_nonexising_key_removal(svc_client_with_repo): +def test_config_view_set_non_existing_key_removal(svc_client_with_repo): """Check that removing a non-existing key (i.e. setting to None) is allowed.""" svc_client, headers, project_id, url_components = svc_client_with_repo @@ -160,7 +234,7 @@ def test_config_view_set_and_show_failures(svc_client_with_repo): """Check errors triggered while invoking config set.""" svc_client, headers, project_id, url_components = svc_client_with_repo - # NOTE: use sections with wrong chars introduces a readin error. Should we handle it at write time? + # NOTE: use sections with wrong chars introduces a reading error. Should we handle it at write time? payload = { "git_url": url_components.href, "config": {".NON_EXISTING": "test"}, From ec3173a8601d8dbf93626778b3da0bdce68e9060 Mon Sep 17 00:00:00 2001 From: Ralf Grubenmann Date: Wed, 17 Jan 2024 10:26:43 +0100 Subject: [PATCH 12/15] feat: add cloud storage support for session start (#3629) --- .pre-commit-config.yaml | 1 + docs/_static/cheatsheet/cheatsheet.json | 2 +- docs/_static/cheatsheet/cheatsheet.pdf | Bin 423502 -> 423500 bytes docs/cheatsheet_hash | 2 +- docs/cheatsheet_json_hash | 2 +- docs/reference/commands/storage.rst | 2 +- pyproject.toml | 2 +- renku/command/checks/storage.py | 2 +- renku/command/command_builder/command.py | 45 ++ renku/command/command_builder/gitlab.py | 54 ++ renku/command/command_builder/repo.py | 35 ++ renku/command/command_builder/storage.py | 46 ++ renku/command/format/storage.py | 59 ++ renku/command/lfs.py | 137 +++++ renku/command/migrate.py | 7 +- renku/command/move.py | 2 +- renku/command/remove.py | 2 +- renku/command/save.py | 2 +- renku/command/session.py | 2 +- renku/command/storage.py | 122 +--- renku/core/dataset/dataset.py | 2 +- renku/core/dataset/dataset_add.py | 2 +- renku/core/dataset/providers/git.py | 2 +- renku/core/dataset/providers/local.py | 2 +- renku/core/dataset/providers/renku.py | 2 +- renku/core/errors.py | 20 +- renku/core/git.py | 2 +- renku/core/init.py | 2 +- .../interface}/git_api_provider.py | 9 +- .../core/interface/storage_service_gateway.py | 58 ++ renku/core/lfs.py | 543 ++++++++++++++++++ renku/core/login.py | 9 +- renku/core/session/docker.py | 5 +- renku/core/session/renkulab.py | 37 +- renku/core/storage.py | 537 +---------------- renku/core/workflow/execute.py | 2 +- renku/core/workflow/plan_factory.py | 2 +- renku/core/workflow/run.py | 2 +- renku/domain_model/cloud_storage.py | 61 ++ .../gitlab_api_provider.py | 72 ++- .../infrastructure/storage/storage_service.py | 154 +++++ renku/ui/cli/__init__.py | 2 + renku/ui/cli/lfs.py | 175 ++++++ renku/ui/cli/storage.py | 113 ++-- renku/ui/service/controllers/api/mixins.py | 4 +- .../controllers/cache_migrations_check.py | 14 +- .../controllers/utils/remote_project.py | 2 + renku/ui/service/views/cache.py | 4 +- renku/ui/service/views/v1/cache.py | 4 +- tests/cli/test_clone.py | 4 +- tests/cli/test_datasets.py | 2 +- tests/cli/test_workflow.py | 12 +- tests/core/commands/test_cli.py | 10 +- tests/core/commands/test_doctor.py | 2 +- .../commands/{test_storage.py => test_lfs.py} | 2 +- tests/core/management/test_storage.py | 2 +- tests/core/test_storage.py | 56 ++ tests/fixtures/common.py | 2 +- tests/fixtures/storage.py | 59 ++ .../controllers/utils/test_remote_project.py | 3 +- tests/utils.py | 3 + 61 files changed, 1726 insertions(+), 800 deletions(-) create mode 100644 renku/command/command_builder/gitlab.py create mode 100644 renku/command/command_builder/storage.py create mode 100644 renku/command/format/storage.py create mode 100644 renku/command/lfs.py rename renku/{ui/service/interfaces => core/interface}/git_api_provider.py (80%) create mode 100644 renku/core/interface/storage_service_gateway.py create mode 100644 renku/core/lfs.py create mode 100644 renku/domain_model/cloud_storage.py rename renku/{ui/service/gateways => infrastructure}/gitlab_api_provider.py (81%) create mode 100644 renku/infrastructure/storage/storage_service.py create mode 100644 renku/ui/cli/lfs.py rename tests/core/commands/{test_storage.py => test_lfs.py} (99%) create mode 100644 tests/core/test_storage.py create mode 100644 tests/fixtures/storage.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ff93c16798..4115f28027 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,6 +43,7 @@ repos: - --ignore=D105,D107,D202,D203,D212,D213,D401,D406,D407,D410,D411,D413 additional_dependencies: - toml + exclude: ^renku/ui/service/views/ - repo: https://github.com/koalaman/shellcheck-precommit rev: v0.8.0 hooks: diff --git a/docs/_static/cheatsheet/cheatsheet.json b/docs/_static/cheatsheet/cheatsheet.json index f1a8709a81..3a2a6ad54f 100644 --- a/docs/_static/cheatsheet/cheatsheet.json +++ b/docs/_static/cheatsheet/cheatsheet.json @@ -441,7 +441,7 @@ ] }, { - "command": "$ renku storage pull ...", + "command": "$ renku lfs pull ...", "description": "Pull 's from external storage (LFS).", "target": [ "rp" diff --git a/docs/_static/cheatsheet/cheatsheet.pdf b/docs/_static/cheatsheet/cheatsheet.pdf index 4246dc60c9bdc1cf7d5b42b3a4d97a43c3d36685..004e86455856ee2aa522840e90c1ed31417da6e2 100644 GIT binary patch delta 6377 zcmajkRWu|3-v)52SF_q$jP7nmcem9w7`eJ z>bw8n{myfq=bZnOh~eSs4zhH{@Ld@rYm-5{Q)d>KE>3GzSm?CK7Fgb1Q$Hn_p-ZYi zZ{3O^_=Bdg^_T4<7H20pjbPJ80c{hWl=pZK8Jic>^iMz7O_k`rP$g`(iHmgJUj#y4 zM^7|*^UMy8@U4IcZ=6tQ9-~Dv58r3cKn*0r%u1?Per&s3rOnQ~RI(&xC#%f42?yw& z`0}t(T^sAH^ue8I$!$zTIv+xkH!V1Djw|8`RfGJd@@Bwde(`pL`5YFFXV;(OPF}I? z7;E!mJ#FSwY9w{%lqIf{m)M!qbxvG$1?Q5M@6ts=>Z0VU^j`#Z&&MaeH?Jj`yUE0A zFz)MK0le%6`(Od*&hHO5E=R$fP$Lv_7>*xT%pmE?#r`A=hZwTeu~ccyHNLxbtz+>1 zWV%mqjK16NmMB^{L-15Ry z*#onuFh;OT`AFUNn%V#>t~Zp$6^pPtc_KgHk04RZjaY5+sc;p!YU48pXB}?FQLysF zyr$T}bf5dzM!AWRs!7V3u)NsV!y))3cOt&h%nvkL*oF1s)hnwUS`v2Y^+6%691oh4 z++eogwULSkm&Gy5;`@Q8(8B)UAWsEb6p>+ zkPp&SvMppxMxH)Ovd)H5G;>`f;3ECKo2x<#4-$*2k~HS)viNjJ)lYl@`n?%d)jJ1L zoXEqrB&(kgQ)T$(?zLffNxb7is0IB^zIQnrYoy``qLL2efgZPqEI_2mS5 z{_|VPMa?Hl9>RDAWI~bIz*L7)<`5;zJGQZ*&It&XUj2kNVrOJI(TF(@04cV6-W5eQ z+T8)-5FU)L6aeY9NY+R2PGs#xYMnt{@lDsqsq+vgEkaboRNA|LT-0Y5GFdk0t1SmC z^tk;vSuBSp^30V7nzo%hCqVwprM<0qHg=Gn0HxZ7znsSGFIiQ1C-SFnl?E$wg_xHsYEr*uqt1UO+L52~ zr3xm4#Mk+}>{RHK;hc-$O4G5GqrjKOmnRA-(Fdr>+NN+pv;9X_5~h?KWtyhoPH|p5 zk9^%JkVTJLewkRobw7v_(!+_uDg3n(}Jr23qO9zdU%noC9r%nTVB*2CwU*La-;UGJF~Bd&2CHDE;$-xKppsLHMP=sY@?>seO-gARSDC~pBYHwMe{8(AmMVE8kPa^DS)_^$iq*IY4 z9P_RwL6PR{FeTpY6|0i#wh%8)Rqt^5ClH$DNm`H{QKB##JKd!u!tq7%6q+D#AZr5C z!C#b%ZL%AEHB4%g3wlQaYKh^@OUtV?SvLTc4x4bMLuUC1HZiTls!{jUM9kYeG$K$% zG^ry3ou}Sfk@MB6spmhCB$;D%f{SGb_~%tcIN~|`Yy`&vPzOTS9Ql_%ISdOwa=oC9 zng{J`&}VV&XV@IK!wb9-n&OYTS&!Aso8$N3+ARjVtJK#RiZvK}>>Lg@Ol^+K_sO0< zjM?!B^9rw&;BN1)j_@{N7RQNq((yC1aL+!QsQV|lnst`SK|S|0+6A94_CECaFc&-- zweFAEg3tHQP4e5)E1mOc#xSm+2>juOSDrCbVbz@(k!s!P%AfEF3f)OX76B%6u&Hz5aC@u zuQza4L!d*k{t6Q%ZI$t;es|uMHfsgZ5?z(5GZ7{K%<#Vaz;)!*1a9Cz(hxYs2_Y%i zMy2rX_r2>Y<|YE7@FBdL990C$_un(rR7cGbCP~=-%zC$^9etiTj~Z%G5IV)_;D0!M zsKUZmQMsQ0H4n00MX0WP{f^D^#zM{RE>m+BT5IG>E9o1GA zL3ULyl9sdMk~?zVDwgzRHa_tA!QG&?l1c4ivu3sC=@lB3H7q&TFnD>lmZ-@)k-WWz z>p3>wNmP+a8}=;Ve-9In$!c-&@f3CAY+_hVhqd@^5{?m;R6iHcW=Q#}6aLJRZ7Z)T zyBLXf0^{sfwqHf}u~5_u(dd|WK!Y8cYk(@FvkuvJ?cuSc&cO?@yGXQ@h+C$?O6EKm zNYpK1>ChVzNfl~rLXX_X(JhT{T}ya=?+6}-V8~`z1b1d5ih$n$DG@5VU89h`qEIgc zjc|OFNdyJG_d{5p=d+29dMajan6{5RXO8JR3wY&~0<}ujTS<>5?Sm54OH4|!zK?2t zH#1wOFelHdd*U>Aqpg5(LQ?!tl`_WWrD{uPY zi*`{TwG6S}bC37}TQu?v+WkRx%*6zs-cCgL*~Y)6oYNi-*hxpuVB`gx=<3NI0EeN3 zrwX;d_q}(t_r8MYi_=BgS!WQG^)RWO?J|Z^td{R~;PJqd>4^kZ=8Ty)C?7 zD_2JtCuDtT)z3ln#Mq~Cn#QaqoAUT}g!bg=t&E!CvfWyaK0_FVhG+=(BrCHGzvBZ(TR;5-`>Pj3qls6$<eY%+#8w~^u@_?DL;t3% zs@bbW2G|u-{IaVzx)ku3ToO-4gc7Mp(H5V}px0`{q}<7;jhe{Zg~SE0 z=p(E?k3uwVHcjqcg2L&t1YZ8>Kg0t6hd6270VatTei0Pi;lwQEGjC6guAgl6U1F~I zUVc!w^gmGqxSHo!&(5Hvy5y>e&59)pMg?TkVyW;;EKp?gHT+MrMbkIlq8((r%>4-*w%Eow zdlj9FPt={7)9~(A>435O0GI7@(V4UMdn*>6IABzkOwzSFjqFOJUxqJ zYXZtvjX3sb>hfDXCvQL#H=e6p6UY#Ll?)zLa$BKf=Hwo3`t{B=?ye{;Am_wrTYt|H7rY1@QG&bl9ckz&I^Hpyl_=n4v9z(P-G%e`Sa`*a%27kTH_;y%ZEHuj zqs5!>Q|2oaYiGDWh(C7mryF|1NIxR{cM-$>%-gu@p8FlBH8Tg+Y9F%7LKac8N0;3x zzQpzK`Ahds3Sc+5pP6oIt?2N8K~8Xyw~Lbmv}zO3>tBcKTDfBH`rh(q^xs{$6~4D( z`L)y}HK1LmnU)OPNmi!lGcn`?;`?Iz+2E~bB?}0h0MXjVREzpJw*abq7=4Sz&#`<@ zcciUp1f_4qGh`Pab~36(q90gz0_~&~_TTeb*W;&ekk|w29Sh^es^d6#ela`^E#*Y< z@wljjx3W|IzR?|AYjWx5TjL zx_yAsdT%CNRKBQ}(>jdcPtRfNtuPL+8cE1P(#!_P!{P@^jh0E3)@XIhWSyuIRs&Qp z@Kv4@)Ls^tQ@n8#p6407u6A^z{zP=&kdkFMRJ!ORvC1HyM`+Kgxe#SOx%0orrXOf% ziJ4_=nS6jn=KqMTjZgOFWTiGeX1{c1*XV+}Jre(?ctD$`g-`PLY9(r&tTL^x$={z6 zrX&Qh_gb3oHmaSwAyJLxU6LJ_8zGfw`rn*9jfl~1_3lNcMG^Nph<|qZ&EZ^$G~GH} z1i29d>m?GMYcbIct9syHx1Vp^3G@RDW^4A`pFnQ?kj>{24ri@_O*fow09*Lm^ zOv1;&1otvtPF;>5+oDmT$p$$~P~S zQ|q)ZN=l>|^PTpXWk^V|uYKcQtLDbmc3~knt2$X*VegUxjY@q)r!{PCMoKDB9|O>) zs`Ck>TBD?HMqA?S_M z8Cv5J@u;7g(J?SEd!^(Vf8an1r62*xO!KYd2RI5cD!|eND0+As%&T z63pB#ho5;NUybgGu`%I&p%d@bs5PU@E44Sh@YV04#Fii&-L;+8XIp~m(US3fSW)~^ z#%$yb3-aY7lPNLR`a`~!zxPXXf@8r1R#UG*uWb`7=k<$Hvu{bTv>!~<)3vw$Mz=eR zZcvkrRlY37mHTl#hHRT*3T*#;;uoyWD5od5MN~pHJlT5u3JyfaHR&=dfqM>9-LgG$ zQL|Kg7}QdwUL#N!`D{b~OdL&Py}b?I6=%-reC)2`yx**%mVCalk$$tc|FJ7rb?z=+}~u#lqqAO%#2oV2B7^4B@?C{#UHZ8C6`1 z)TqSbPd$BMDSd`A8Wv(R(2iZq*W=@32mD03_p&qY5&U93M1(}c6J9&dt(Kmw(zu%Z zV``Q6A2yAb`Z$_?Mv{=@K%8Hn=0A2o(_QoSZ?f^K- zV#*;9vg?*O(UzEsmT(l4L2Q#=H8!jFqv`z^=a#Y0zQOqWmv|Mv1=U>jHEA2;BavNq zA5V_A(N3=57nEKdRICQ<<{Ur2yBO93+j(7cWnbT&$52LLfLv_;_iq7$b9Aeob-! delta 6395 zcmajkMNplA(k5UWLh#`3?(PyexDzC3fP=eBfDd;F?jATCBsjs{-3cxsxVyuDtNvNs zsaZ^~d-qg#yDXd;?yLtAu`ZSqv3Rn*? za0TJ#@Mu|YJy^~t;$<1(a{{&x%71iQ#4&l6sjN#p2r&p#@~yjW8%+kV3cBR`??_8y)1{%_Z8gIno%I-7-79%?F@j> zS0zm^i?0W3ms<@1vslSUl7Ag(}Q0EJe7KAEN`{?46lMIY5UD2&QvmB!etV;-XqT3ocdm`X}qh z^griHiM(s#)#LAxK^0|T2Qj6zW5L{SWY9W-I*Pu7l_*viO6B5+Ld2}fTuNq7M^H#H zMhH5n6auexNlVy{f;8li8b`!?`^QJ;`>6N0f81FjwR11(nnomrHa}rC4g|oQ;U^q7 z=qL9`W>Hb5a+;Rx)R$0sE#jCD)0A%gawK^LP$kmV@fLLle&627?Tu6+JxD1m>7Wo- zWuYaEwiBMhfCB|Z%{ysY-I_ZL*VH96rDqRg&BnC*V?SH@>AO%g{wcv|y3+Fa{r6x^ z>-?gBLM8R9WlqG1_wLB#ZX6H<=lz$+9rI<-tT2zk^RHN`c0?J*<|Am4N=~>S$!!6b z!-qeS)a*2|!6_8JC;m>MSpAdD3<>l$ab9Z zYPn%&gN~%UzAN0TXHwHhSW|5fldxgK#%of`0Y@bCRy-GIK+Hd%$!9MI3E-)vbvl>^PVpP(r|8^7;N|@MywQP*Q z$wG}SdH@|1y62kD3qS^W(%Df}NwBZ7O}mlpmytAVPQ?*40iSnCA7G^YHMyywuo^s zPH-O|bnJ>30c^|mr8KjL)6!U5@8WA2>_f|E9Kwto^vc@jx0yPX)nd3udcJz?4z4Vj zv!giBZH)QoBaAGN`HSf74E)QB`!O`F?E6IuQ2sTDQ>y=b-y_tla|F$MUtxP@1*a(9*Z&fhx$AG#%gdrX zn~G3V0@z9_MCxJ!vfMU1h-%!BQp=M$Yzgft zU`jGElzJyaEJQ=NTYPFORzssp0B(N*YA*o3#u2a?N)d6`5Lz)`@8g5tZ>YO&*{-cd z1V`p7oi>sYHk$W~{QXP97j(Lm)#GSu>C|Sn+E73)0-t4v7jsiZ^wp0i&1V%iK`k2| z_`r;S2V=RZ=RBbYNpShx%1e{aj=H&2mNP_SX(u@Lth^c8JZLDN92ofW zRv3C_>i-Ws>sr{WV@v-j8hhiLWIJS^=R(}j91Ff7dPJ_dVzs-e>u-F<~LG>w$d zlx17f{_C%SR<@_MthtweL`_BJD@Tp{ftu4@fBS`XOS{WeGwCb%jZ?4WSX2N_@d~_AuhMPUgUtIfZ zIwC=m%lYJ1Y3J#`cc*F35T0u1g;9)TFk4HUH z3F6IEq7t&1j6LavyT`1+u`9ajnnsMBlUj3^=16sBVuP z72jv>oCh*>e$2mvYzZ3y{Bc7?wvhA8@aIfb(H9cV>Vn3F(GO9V;ewm^MC!`B4lG9Q z*gt1<w7*UhIDZWDy?wLmiZSL%vuLsDIBXS9pL+$^Q#%KM=^TD1%6XLOZfKljJ#nBu>JA zXwjkMD*JZYr)4VBGdHB6jm8bq;j;v_Zk6$_$9M}9HTXtVAEaoIis6=jVwd%$(U&Nm z`#``9f1vpVn*M{&XNvDI10Wf--19C^pECnKJ5HuGclZUf{_2Ib9!K5C7{du z)W&q$R4XooybhSuR-|1@;Jc$aJhF=+Axs)6xam*chLeYrk7l5xaK zim7GJ?UrAvQx<&pi^lYxqDZk}1}n{#XCuZ}ro!1y&usz%gaRup-yd1kZuV|6_On|^ z+UgjGxtWKkYVwil$Bmo}t*Yq-Wmi*c&FhW6eml9?yw{^T^*em`bi~|Wz$XXQj(g&8 zG15nqs4Er&v`|__yCNB^UDQx!5Qbvp5q#obkxLZq6c{?n56n{Yxo)sU%!Ut6s0a&+ zpA?`v$m*!n3!--^A%3ZF2tYLHW&d%d( z3D{6L7a8B)6r6u_>h#0;)5FP;^A7;dDDNJ%LZD9_U_zVEY2{zsw!v|6vyRza}zC$|+d#Kg=)DV!v(BPT+#0s0yJ` z4)!E75Z%z3q?9mL?~6@>y+VgIgyE*~c7Glb&pP8ukL3@~ee*twxq@k}ob!uT$;pD# zW%FrYiWsjQX#@7P7xg^rMzWeJsZ9X~aTDWvHKx*%Ovm++-&Ndz*z&oKD>JGvwXc_r zJT&o7%+0LlmtVKF_}49Tp&h3KtCNQP(uVWMzNB{>DpHVnhRYf?%?*Vqm1}9dLsdZ? z+}fPbj&^paLtTzCL=8NV8vruvJkkXxvY)+03=5N9pN~5?9Q`C4O2*bg%L>9TKxjZ< z%P5A|528&z5Bv>@7pSom(7*2tz#%N0u)R&YqaNSFttF{(yA3s4a zIfs?btL%YjaARzS>;M9<;(#)&Ul|PUO)=>R`c+>dEh9op0ba7%R6s0$1y*Ps?iaI^ z%H~wlf~p0si7h(VZQr%scO8s$QRRIH!RvaiSLg-GH5N9;wWe*WijS6nG`>Wa2h3Do z(|@+5sxrX)-!2Qf2D+>AI02g`Gfpv{7@C`Hd<+a$=%M}DU{fyTx{8^Q8WCBM-~Y^` z&|aQj73*rr*hyS7B4A}I+R9tmpITc-IVTa`c$G4_m5D6{lbMoxx#%+Xf|*9p$V)O* zxaP*~6xxH`CR5Bme=EvVhcnAy255)`Llw#9GVEgYaPSplt$G!vlt)7a3d~9J^&xDZ zm_qpum@;tV#4Yr>Mrx~j7Y)rZcAEPbzfqkm64TJP*6X66R_!Pl!7e6Fh7)1}doBB>8!VelMD0$A8ANBp3P5<&xBEj_jk5 zXU6+r#n#)m9TogKO}WJo!HwKT7mJ6IEtL}%@qRJS^q`hUW4uOkccksQmoY#f{QdQb zBf}rffs1Jcj%t%NZzg?78T0sS2Ps>$OSeFe?bYMIt1i>EV0Y2|aWA_eH|zBEV`mJj zhaPU}%WBH%L{#ZuStod@hN<=I*}^mE#BqrMFg`Axt~EWykz0@r$Qcf@YNjVeC& z>q^<2Y^xQ=W8Q{S$us8`bEL5PKV68*T?EmzNzytJ?|<>|>vT8vSi`!C*76)u;1qfj zMJs&AqrYt~R1ag$gymCtmYhzB)kdukjT4*9THwBW`oU!fxU3p4EVxzfh%e>^hpQ

ul5JOM@tCxIwH;H!VG1Gh0!Z|-LaP+kRDgW)_T_4xEjc@-W})bb1r&q!Wk=c_#zjPER$g^c0$(F*Q79Nyy^!(cbj8>~J$1{6=~*{T!KK8DU}14bRRW`^+Qk=f}l(FCJ zIdDT}tyKUK=ZgInM$tVH5yvbZa5RGR4S)2xo?Biu8Y*{=J1tdzPg3e(Qv>3PjEUul zEfkhSfF~l_ur!)f#X{kY{g>*87+(-Wj5>Pjbyx5om*u@&3W{R|dG;)4*Y(L!R7Ve{ zh$DZqV^O1_((6~O`~Av1%@+&HoKX+Ub{U5tngahbo|sUl+{v)<>FIx;tuSC4@Ern& zZnIVliwhXb<6Du74Ufh@rYhwV9l8$iIB0w*05$}muc;7m#5%^C;`$aif~%^VG>sVW zv=gDZs{?nrBX1R~J8O$-ErBl*3ZY(fwvIvG$6YcEabsSGxsKwSBG(|Ihl>)?rysZj zB_ToHmAEqQnX=|^DwG9(W!_DNB7@s0kxaSa)oSsId%BGTgtsMAk_<<$*=>`w<{6o- z0W5S6o28{BHc{z&M4F!1)cO5hOZ-k^HYR%`TzRU>hG?y&oP`$mf^Ij2ViK29B9%63 z`FXou3c^67YJhi_Tk}VzhCFPJe3= zne>U7V9d5vAZH7je(Hd58#BN<&V=p!MNBS#t3zH~<4^0#WqW{#=#w66QscA^z`n#s zL&R&oBJW(rWZ;?XFT+dJ12LK2CeA4sXj50M)-epJo>QZ<1qXsW+Q`%gmjsy35fb0Z zc=jaaxcg^VhC*zGKgKc))tg1Pe{d&WdGo~oHfN);pyEAnD0ih))oK!ld>C~6r{XCI zHH)ZpRfLCiEs75N*D`wGMlKpSpt(|M}z>*rs>ZWq@d_DXF3IsC%O; z<7#a3g>DJH`z_@tBFK%K^yPTsIX_%okB@I9-i*RpQmN$yUnIVZ)H6XQ0F8U2n4~|_ zJ&y4FH;lBXmp9j%{q{ih7Ig!cTK9lhB<>TJ=&pf;h`bMV3QLXPCAwaYx*w8W-qL&9 zh1vNwNP>bRU*hfR$31~aq*%p1$ODt@Xm^^rMGoTfw9A8@a8(yI_@*B3!wa=r%W!fr z&kxF$^Ll5WfA|jNVp)HOFQxgYnI7Pd#QK@X+{}X8oL@iyYymd6;1c4rHHY6>Rfs-8a?mI&8vhbgxrdGg-1eb(3*l#gMNB`4WpR=+@I}aTBv2Fjtk=W5R~zmTdkKTd)eA>%`cZ8@wd "Command": return RequireClean(self) + @check_finalized + def require_login(self) -> "Command": + """Check that the user is logged in.""" + from renku.command.command_builder.repo import RequireLogin + + return RequireLogin(self) + @check_finalized def with_communicator(self, communicator: CommunicationCallback) -> "Command": """Create a communicator. @@ -479,6 +487,20 @@ def with_database(self, write: bool = False, path: Optional[str] = None, create: return DatabaseCommand(self, write, path, create) + @check_finalized + def with_gitlab_api(self) -> "Command": + """Inject gitlab api client.""" + from renku.command.command_builder.gitlab import GitlabApiCommand + + return GitlabApiCommand(self) + + @check_finalized + def with_storage_api(self) -> "Command": + """Inject storage api client.""" + from renku.command.command_builder.storage import StorageApiCommand + + return StorageApiCommand(self) + class CommandResult: """The result of a command. @@ -496,3 +518,26 @@ def __init__(self, output, error, status) -> None: self.output = output self.error = error self.status = status + + +class RequireExecutable(Command): + """Builder to check if an executable is installed.""" + + HOOK_ORDER = 4 + + def __init__(self, builder: Command, executable: str) -> None: + """__init__ of RequireExecutable.""" + self._builder = builder + self._executable = executable + + def _pre_hook(self, builder: Command, context: dict, *args, **kwargs) -> None: + """Check if an executable exists on the system. + + Args: + builder(Command): Current ``CommandBuilder``. + context(dict): Current context. + """ + if not shutil.which(self._executable): + raise errors.ExecutableNotFound( + f"Couldn't find the executable '{self._executable}' on this system. Please make sure it's installed" + ) diff --git a/renku/command/command_builder/gitlab.py b/renku/command/command_builder/gitlab.py new file mode 100644 index 0000000000..878ac14501 --- /dev/null +++ b/renku/command/command_builder/gitlab.py @@ -0,0 +1,54 @@ +# Copyright Swiss Data Science Center (SDSC). A partnership between +# École Polytechnique Fédérale de Lausanne (EPFL) and +# Eidgenössische Technische Hochschule Zürich (ETHZ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Command builder for gitlab api.""" + + +from renku.command.command_builder.command import Command, check_finalized +from renku.core.interface.git_api_provider import IGitAPIProvider +from renku.domain_model.project_context import project_context +from renku.infrastructure.gitlab_api_provider import GitlabAPIProvider + + +class GitlabApiCommand(Command): + """Builder to get a gitlab api client.""" + + PRE_ORDER = 4 + + def __init__(self, builder: Command) -> None: + self._builder = builder + + def _injection_pre_hook(self, builder: Command, context: dict, *args, **kwargs) -> None: + """Create a gitlab api provider.""" + + if not project_context.has_context(): + raise ValueError("Gitlab API builder needs a ProjectContext to be set.") + + def _get_provider(): + from renku.core.login import read_renku_token + + token = read_renku_token(None, True) + if not token: + return None + return GitlabAPIProvider(token=token) + + context["constructor_bindings"][IGitAPIProvider] = _get_provider + + @check_finalized + def build(self) -> Command: + """Build the command.""" + self._builder.add_injection_pre_hook(self.PRE_ORDER, self._injection_pre_hook) + + return self._builder.build() diff --git a/renku/command/command_builder/repo.py b/renku/command/command_builder/repo.py index 3778ef5387..6bbc41c734 100644 --- a/renku/command/command_builder/repo.py +++ b/renku/command/command_builder/repo.py @@ -21,6 +21,7 @@ from renku.command.command_builder.command import Command, CommandResult, check_finalized from renku.core import errors from renku.core.git import ensure_clean +from renku.core.login import ensure_login from renku.domain_model.project_context import project_context @@ -42,6 +43,7 @@ def __init__( """__init__ of Commit. Args: + builder(Command): The current ``CommandBuilder``. message (str): The commit message. Auto-generated if left empty (Default value = None). commit_if_empty (bool): Whether to commit if there are no modified files (Default value = None). raise_if_empty (bool): Whether to raise an exception if there are no modified files (Default value = None). @@ -164,6 +166,39 @@ def build(self) -> Command: return self._builder.build() +class RequireLogin(Command): + """Builder to check if a user is logged in.""" + + HOOK_ORDER = 4 + + def __init__(self, builder: Command) -> None: + """__init__ of RequireLogin.""" + self._builder = builder + + def _pre_hook(self, builder: Command, context: dict, *args, **kwargs) -> None: + """Check if the user is logged in. + + Args: + builder(Command): Current ``CommandBuilder``. + context(dict): Current context. + """ + if not project_context.has_context(): + raise ValueError("RequireLogin builder needs a ProjectContext to be set.") + + ensure_login() + + @check_finalized + def build(self) -> Command: + """Build the command. + + Returns: + Command: Finalized version of this command. + """ + self._builder.add_pre_hook(self.HOOK_ORDER, self._pre_hook) + + return self._builder.build() + + class Isolation(Command): """Builder to run a command in git isolation.""" diff --git a/renku/command/command_builder/storage.py b/renku/command/command_builder/storage.py new file mode 100644 index 0000000000..3cc1f808ac --- /dev/null +++ b/renku/command/command_builder/storage.py @@ -0,0 +1,46 @@ +# Copyright Swiss Data Science Center (SDSC). A partnership between +# École Polytechnique Fédérale de Lausanne (EPFL) and +# Eidgenössische Technische Hochschule Zürich (ETHZ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Command builder for storage api.""" + + +from renku.command.command_builder.command import Command, check_finalized +from renku.core.interface.storage_service_gateway import IStorageService +from renku.domain_model.project_context import project_context +from renku.infrastructure.storage.storage_service import StorageService + + +class StorageApiCommand(Command): + """Builder to get a storage api client.""" + + PRE_ORDER = 4 + + def __init__(self, builder: Command) -> None: + self._builder = builder + + def _injection_pre_hook(self, builder: Command, context: dict, *args, **kwargs) -> None: + """Create a storage api provider.""" + + if not project_context.has_context(): + raise ValueError("storage api builder needs a ProjectContext to be set.") + + context["constructor_bindings"][IStorageService] = lambda: StorageService() + + @check_finalized + def build(self) -> Command: + """Build the command.""" + self._builder.add_injection_pre_hook(self.PRE_ORDER, self._injection_pre_hook) + + return self._builder.build() diff --git a/renku/command/format/storage.py b/renku/command/format/storage.py new file mode 100644 index 0000000000..7c0fc5124d --- /dev/null +++ b/renku/command/format/storage.py @@ -0,0 +1,59 @@ +# Copyright Swiss Data Science Center (SDSC). A partnership between +# École Polytechnique Fédérale de Lausanne (EPFL) and +# Eidgenössische Technische Hochschule Zürich (ETHZ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Serializers for storage.""" + +import json +from typing import List, Optional + +from renku.command.format.tabulate import tabulate +from renku.domain_model.cloud_storage import CloudStorage + + +def tabular(cloud_storages: List[CloudStorage], *, columns: Optional[str] = None): + """Format cloud_storages with a tabular output.""" + if not columns: + columns = "id,name,private,type" + return tabulate(collection=cloud_storages, columns=columns, columns_mapping=CLOUD_STORAGE_COLUMNS) + + +def log(cloud_storages: List[CloudStorage], *, columns: Optional[str] = None): + """Format cloud_storages in a log like output.""" + from renku.ui.cli.utils.terminal import style_header, style_key + + output = [] + + for cloud_storage in cloud_storages: + output.append(style_header(f"CloudStorage {cloud_storage.name}")) + output.append(style_key("Id: ") + cloud_storage.storage_id) # type: ignore + output.append(style_key("Source Path: ") + cloud_storage.source_path) + output.append(style_key("Target path: ") + cloud_storage.target_path) + output.append(style_key("Private: ") + ("Yes" if cloud_storage.private else "No")) + output.append(style_key("Configuration: \n") + json.dumps(cloud_storage.configuration, indent=4)) + output.append("") + return "\n".join(output) + + +CLOUD_STORAGE_FORMATS = {"tabular": tabular, "log": log} +"""Valid formatting options.""" + +CLOUD_STORAGE_COLUMNS = { + "id": ("storage_id", "id"), + "name": ("name", "name"), + "source_path": ("source_path", "source path"), + "target_path": ("target_path", "target path"), + "private": ("private", "private"), + "type": ("storage_type", "type"), +} diff --git a/renku/command/lfs.py b/renku/command/lfs.py new file mode 100644 index 0000000000..04fc79e498 --- /dev/null +++ b/renku/command/lfs.py @@ -0,0 +1,137 @@ +# +# Copyright 2018-2023- Swiss Data Science Center (SDSC) +# A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and +# Eidgenössische Technische Hochschule Zürich (ETHZ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Renku storage command.""" + +from typing import List + +from pydantic import ConfigDict, validate_call + +from renku.command.command_builder.command import Command +from renku.core.lfs import ( + check_lfs_migrate_info, + check_requires_tracking, + clean_storage_cache, + migrate_files_to_lfs, + pull_paths_from_storage, +) +from renku.core.util import communication +from renku.domain_model.project_context import project_context + + +@validate_call(config=ConfigDict(arbitrary_types_allowed=True)) +def _check_lfs(everything: bool = False): + """Check if large files are not in lfs. + + Args: + everything: Whether to check whole history (Default value = False). + + Returns: + List of large files. + """ + files = check_lfs_migrate_info(everything) + + if files: + communication.warn("Git history contains large files\n\t" + "\n\t".join(files)) + + return files + + +def check_lfs_command(): + """Check lfs command.""" + return Command().command(_check_lfs) + + +@validate_call(config=ConfigDict(arbitrary_types_allowed=True)) +def _fix_lfs(paths: List[str]): + """Migrate large files into lfs. + + Args: + paths(List[str]): Paths to migrate to LFS. + """ + migrate_files_to_lfs(paths) + + +def fix_lfs_command(): + """Fix lfs command.""" + return ( + Command() + .command(_fix_lfs) + .require_clean() + .require_migration() + .with_database(write=True) + .with_commit(commit_if_empty=False) + ) + + +@validate_call(config=ConfigDict(arbitrary_types_allowed=True)) +def _pull(paths: List[str]): + """Pull the specified paths from external storage. + + Args: + paths(List[str]): Paths to pull from LFS. + """ + pull_paths_from_storage(project_context.repository, *paths) + + +def pull_command(): + """Command to pull the specified paths from external storage.""" + return Command().command(_pull) + + +@validate_call(config=ConfigDict(arbitrary_types_allowed=True)) +def _clean(paths: List[str]): + """Remove files from lfs cache/turn them back into pointer files. + + Args: + paths:List[str]: Paths to turn back to pointer files. + """ + untracked_paths, local_only_paths = clean_storage_cache(*paths) + + if untracked_paths: + communication.warn( + "These paths were ignored as they are not tracked" + + " in git LFS:\n\t{}\n".format("\n\t".join(untracked_paths)) + ) + + if local_only_paths: + communication.warn( + "These paths were ignored as they are not pushed to " + + "a remote with git LFS:\n\t{}\n".format("\n\t".join(local_only_paths)) + ) + + +def clean_command(): + """Command to remove files from lfs cache/turn them back into pointer files.""" + return Command().command(_clean) + + +@validate_call(config=ConfigDict(arbitrary_types_allowed=True)) +def _check_lfs_hook(paths: List[str]): + """Check if paths should be in LFS. + + Args: + paths(List[str]): Paths to check + + Returns: + List of files that should be in LFS. + """ + return check_requires_tracking(*paths) + + +def check_lfs_hook_command(): + """Command to pull the specified paths from external storage.""" + return Command().command(_check_lfs_hook) diff --git a/renku/command/migrate.py b/renku/command/migrate.py index 7b9155ab58..2dcb9d0c17 100644 --- a/renku/command/migrate.py +++ b/renku/command/migrate.py @@ -21,6 +21,7 @@ from pydantic import ConfigDict, validate_call from renku.command.command_builder.command import Command +from renku.core import errors from renku.core.errors import MinimumVersionError from renku.core.migration.migrate import SUPPORTED_PROJECT_VERSION from renku.domain_model.project_context import project_context @@ -184,7 +185,11 @@ def _template_migration_check() -> TemplateStatusResult: from renku.core.config import get_value from renku.core.template.usecase import check_for_template_update - project = project_context.project + try: + project = project_context.project + except ValueError: + raise errors.MigrationRequired() + template_source = project.template_metadata.template_source template_ref = project.template_metadata.template_ref template_id = project.template_metadata.template_id diff --git a/renku/command/move.py b/renku/command/move.py index a5f68f9e58..21a0742e26 100644 --- a/renku/command/move.py +++ b/renku/command/move.py @@ -27,7 +27,7 @@ from renku.core.dataset.dataset import move_files from renku.core.dataset.datasets_provenance import DatasetsProvenance from renku.core.interface.dataset_gateway import IDatasetGateway -from renku.core.storage import track_paths_in_storage, untrack_paths_from_storage +from renku.core.lfs import track_paths_in_storage, untrack_paths_from_storage from renku.core.util import communication from renku.core.util.metadata import is_protected_path from renku.core.util.os import get_relative_path, is_subpath diff --git a/renku/command/remove.py b/renku/command/remove.py index a247159cc6..8ae0974076 100644 --- a/renku/command/remove.py +++ b/renku/command/remove.py @@ -27,7 +27,7 @@ from renku.core import errors from renku.core.dataset.datasets_provenance import DatasetsProvenance from renku.core.interface.dataset_gateway import IDatasetGateway -from renku.core.storage import check_external_storage, untrack_paths_from_storage +from renku.core.lfs import check_external_storage, untrack_paths_from_storage from renku.core.util import communication from renku.core.util.git import get_git_user from renku.core.util.os import delete_dataset_file, expand_directories diff --git a/renku/command/save.py b/renku/command/save.py index 961cffe721..5e531bff09 100644 --- a/renku/command/save.py +++ b/renku/command/save.py @@ -21,7 +21,7 @@ from renku.command.command_builder.command import Command from renku.core import errors -from renku.core.storage import track_paths_in_storage +from renku.core.lfs import track_paths_in_storage from renku.domain_model.project_context import project_context diff --git a/renku/command/session.py b/renku/command/session.py index 62a0d1cd4f..71e35d9bbe 100644 --- a/renku/command/session.py +++ b/renku/command/session.py @@ -52,7 +52,7 @@ def session_list_command(): def session_start_command(): """Start an interactive session.""" - return Command().command(session_start).with_database().require_migration() + return Command().command(session_start).with_database().require_migration().with_gitlab_api().with_storage_api() def session_stop_command(): diff --git a/renku/command/storage.py b/renku/command/storage.py index 64679c553e..661df99251 100644 --- a/renku/command/storage.py +++ b/renku/command/storage.py @@ -13,124 +13,12 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""Renku storage command.""" - -from typing import List - -from pydantic import ConfigDict, validate_call - +"""Cloud storage commands.""" from renku.command.command_builder.command import Command -from renku.core.storage import ( - check_lfs_migrate_info, - check_requires_tracking, - clean_storage_cache, - migrate_files_to_lfs, - pull_paths_from_storage, -) -from renku.core.util import communication -from renku.domain_model.project_context import project_context - - -@validate_call(config=ConfigDict(arbitrary_types_allowed=True)) -def _check_lfs(everything: bool = False): - """Check if large files are not in lfs. - - Args: - everything: Whether to check whole history (Default value = False). - - Returns: - List of large files. - """ - files = check_lfs_migrate_info(everything) - - if files: - communication.warn("Git history contains large files\n\t" + "\n\t".join(files)) - - return files - - -def check_lfs_command(): - """Check lfs command.""" - return Command().command(_check_lfs) - - -@validate_call(config=ConfigDict(arbitrary_types_allowed=True)) -def _fix_lfs(paths: List[str]): - """Migrate large files into lfs. - - Args: - paths(List[str]): Paths to migrate to LFS. - """ - migrate_files_to_lfs(paths) - - -def fix_lfs_command(): - """Fix lfs command.""" - return ( - Command() - .command(_fix_lfs) - .require_clean() - .require_migration() - .with_database(write=True) - .with_commit(commit_if_empty=False) - ) - - -@validate_call(config=ConfigDict(arbitrary_types_allowed=True)) -def _pull(paths: List[str]): - """Pull the specified paths from external storage. - - Args: - paths(List[str]): Paths to pull from LFS. - """ - pull_paths_from_storage(project_context.repository, *paths) - - -def pull_command(): - """Command to pull the specified paths from external storage.""" - return Command().command(_pull) - - -@validate_call(config=ConfigDict(arbitrary_types_allowed=True)) -def _clean(paths: List[str]): - """Remove files from lfs cache/turn them back into pointer files. - - Args: - paths:List[str]: Paths to turn back to pointer files. - """ - untracked_paths, local_only_paths = clean_storage_cache(*paths) - - if untracked_paths: - communication.warn( - "These paths were ignored as they are not tracked" - + " in git LFS:\n\t{}\n".format("\n\t".join(untracked_paths)) - ) - - if local_only_paths: - communication.warn( - "These paths were ignored as they are not pushed to " - + "a remote with git LFS:\n\t{}\n".format("\n\t".join(local_only_paths)) - ) - - -def clean_command(): - """Command to remove files from lfs cache/turn them back into pointer files.""" - return Command().command(_clean) - - -@validate_call(config=ConfigDict(arbitrary_types_allowed=True)) -def _check_lfs_hook(paths: List[str]): - """Check if paths should be in LFS. - - Args: - paths(List[str]): Paths to check - Returns: - List of files that should be in LFS. - """ - return check_requires_tracking(*paths) +def list_storage_command(): + """Command to list configured cloud storage.""" + from renku.core.storage import list_storage -def check_lfs_hook_command(): - """Command to pull the specified paths from external storage.""" - return Command().command(_check_lfs_hook) + return Command().command(list_storage).with_database().require_login().with_gitlab_api().with_storage_api() diff --git a/renku/core/dataset/dataset.py b/renku/core/dataset/dataset.py index 1b8180fc6b..1f5511c541 100644 --- a/renku/core/dataset/dataset.py +++ b/renku/core/dataset/dataset.py @@ -39,7 +39,7 @@ from renku.core.dataset.tag import get_dataset_by_tag, prompt_access_token, prompt_tag_selection from renku.core.image import ImageObjectRequest from renku.core.interface.dataset_gateway import IDatasetGateway -from renku.core.storage import check_external_storage, track_paths_in_storage +from renku.core.lfs import check_external_storage, track_paths_in_storage from renku.core.util import communication from renku.core.util.datetime8601 import local_now from renku.core.util.git import get_git_user diff --git a/renku/core/dataset/dataset_add.py b/renku/core/dataset/dataset_add.py index 46985a1eaf..fa5491c94d 100644 --- a/renku/core/dataset/dataset_add.py +++ b/renku/core/dataset/dataset_add.py @@ -33,7 +33,7 @@ from renku.core.dataset.providers.models import DatasetAddAction, DatasetAddMetadata from renku.core.interface.dataset_gateway import IDatasetGateway from renku.core.interface.storage import IStorage -from renku.core.storage import check_external_storage, track_paths_in_storage +from renku.core.lfs import check_external_storage, track_paths_in_storage from renku.core.util import communication, requests from renku.core.util.git import get_git_user from renku.core.util.os import get_absolute_path, get_file_size, get_files, get_relative_path, hash_file, is_subpath diff --git a/renku/core/dataset/providers/git.py b/renku/core/dataset/providers/git.py index 1ed53eecd8..6d5b39c667 100644 --- a/renku/core/dataset/providers/git.py +++ b/renku/core/dataset/providers/git.py @@ -25,7 +25,7 @@ from renku.core import errors from renku.core.dataset.pointer_file import create_external_file from renku.core.dataset.providers.api import AddProviderInterface, ProviderApi, ProviderPriority -from renku.core.storage import pull_paths_from_storage +from renku.core.lfs import pull_paths_from_storage from renku.core.util import communication from renku.core.util.git import clone_repository, get_cache_directory_for_repository from renku.core.util.metadata import is_linked_file diff --git a/renku/core/dataset/providers/local.py b/renku/core/dataset/providers/local.py index 538b9f8576..8792d67011 100644 --- a/renku/core/dataset/providers/local.py +++ b/renku/core/dataset/providers/local.py @@ -30,7 +30,7 @@ ProviderApi, ProviderPriority, ) -from renku.core.storage import check_external_storage, track_paths_in_storage +from renku.core.lfs import check_external_storage, track_paths_in_storage from renku.core.util import communication from renku.core.util.metadata import is_protected_path from renku.core.util.os import get_absolute_path, get_safe_relative_path, is_path_empty, is_subpath diff --git a/renku/core/dataset/providers/renku.py b/renku/core/dataset/providers/renku.py index 90398d022f..a7b2fc8142 100644 --- a/renku/core/dataset/providers/renku.py +++ b/renku/core/dataset/providers/renku.py @@ -25,8 +25,8 @@ from renku.core import errors from renku.core.dataset.datasets_provenance import DatasetsProvenance from renku.core.dataset.providers.api import ImporterApi, ImportProviderInterface, ProviderApi, ProviderPriority +from renku.core.lfs import pull_paths_from_storage from renku.core.login import read_renku_token -from renku.core.storage import pull_paths_from_storage from renku.core.util import communication from renku.core.util.git import clone_renku_repository, get_cache_directory_for_repository, get_file_size from renku.core.util.metadata import is_external_file, make_project_temp_dir diff --git a/renku/core/errors.py b/renku/core/errors.py index 94ca5d608f..46976d5852 100644 --- a/renku/core/errors.py +++ b/renku/core/errors.py @@ -59,6 +59,14 @@ class NotFound(RenkuException): """Raise when an object is not found in KG.""" +class NotLoggedIn(RenkuException): + """Raised when a user is not logged in to a Renku platform.""" + + +class ExecutableNotFound(RenkuException): + """Raised when an executable wasn't found on the system.""" + + class ParameterError(RenkuException): """Raise in case of invalid parameter.""" @@ -159,11 +167,15 @@ def __init__(self, ignored: List[Union[Path, str]]): class MigrationRequired(RenkuException): """Raise when migration is required.""" - def __init__(self): + def __init__(self, msg: Optional[str] = None): """Build a custom message.""" - super().__init__( - "Project version is outdated and a migration is required.\n" "Run `renku migrate` command to fix the issue." - ) + if not msg: + msg = ( + "Project version is outdated and a migration is required.\n" + "Run `renku migrate` command to fix the issue." + ) + + super().__init__(msg) class ProjectNotSupported(RenkuException): diff --git a/renku/core/git.py b/renku/core/git.py index 1debddeb08..d851d0f0d2 100644 --- a/renku/core/git.py +++ b/renku/core/git.py @@ -24,7 +24,7 @@ from typing import Any, Optional, Tuple, Type from renku.core import errors -from renku.core.storage import checkout_paths_from_storage +from renku.core.lfs import checkout_paths_from_storage from renku.core.util.contexts import Isolation from renku.core.util.git import get_dirty_paths from renku.core.util.os import get_absolute_path diff --git a/renku/core/init.py b/renku/core/init.py index b6d6446b30..166978d22f 100644 --- a/renku/core/init.py +++ b/renku/core/init.py @@ -32,9 +32,9 @@ from renku.core.githooks import install_githooks from renku.core.image import ImageObjectRequest from renku.core.interface.database_gateway import IDatabaseGateway +from renku.core.lfs import init_external_storage, storage_installed from renku.core.migration.utils import OLD_METADATA_PATH from renku.core.project import set_project_image -from renku.core.storage import init_external_storage, storage_installed from renku.core.template.template import ( FileAction, RenderedTemplate, diff --git a/renku/ui/service/interfaces/git_api_provider.py b/renku/core/interface/git_api_provider.py similarity index 80% rename from renku/ui/service/interfaces/git_api_provider.py rename to renku/core/interface/git_api_provider.py index bd8407d7aa..8eb6907849 100644 --- a/renku/ui/service/interfaces/git_api_provider.py +++ b/renku/core/interface/git_api_provider.py @@ -23,14 +23,21 @@ class IGitAPIProvider(ABC): """Interface a Git API Provider.""" + def __init__(self, token: str): + """Initialize class.""" + raise NotImplementedError() + def download_files_from_api( self, files: List[Union[Path, str]], folders: List[Union[Path, str]], target_folder: Union[Path, str], remote: str, - token: str, branch: Optional[str] = None, ): """Download files through a remote Git API.""" raise NotImplementedError() + + def get_project_id(self, gitlab_url: str, namespace: str, name: str) -> Optional[str]: + """Get a gitlab project id from namespace/name.""" + raise NotImplementedError() diff --git a/renku/core/interface/storage_service_gateway.py b/renku/core/interface/storage_service_gateway.py new file mode 100644 index 0000000000..05e45bdcaa --- /dev/null +++ b/renku/core/interface/storage_service_gateway.py @@ -0,0 +1,58 @@ +# Copyright Swiss Data Science Center (SDSC) +# A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and +# Eidgenössische Technische Hochschule Zürich (ETHZ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Interface for a remote storage service.""" + +from typing import List, Optional, Protocol, runtime_checkable + +from renku.domain_model.cloud_storage import CloudStorage, CloudStorageWithSensitiveFields + + +@runtime_checkable +class IStorageService(Protocol): + """Interface for a storage service.""" + + @property + def project_id(self) -> Optional[str]: + """Get the current gitlab project id. + + Note: This is mostly a workaround since storage service is already done to only accept + project ids, but the CLI knows nothing about those. + This could should be removed once we move to proper renku projects. + """ + ... + + def list(self, project_id: str) -> List[CloudStorageWithSensitiveFields]: + """List storage configured for the current project.""" + ... + + def create(self, storage: CloudStorage) -> CloudStorageWithSensitiveFields: + """Create a new cloud storage.""" + ... + + def edit(self, storage_id: str, new_storage: CloudStorage) -> CloudStorageWithSensitiveFields: + """Edit a cloud storage.""" + ... + + def delete(self, storage_id: str) -> None: + """Delete a cloud storage.""" + ... + + def validate(self, storage: CloudStorage) -> None: + """Validate a cloud storage. + + Raises an exception for invalid storage. + """ + ... diff --git a/renku/core/lfs.py b/renku/core/lfs.py new file mode 100644 index 0000000000..c42df05e2f --- /dev/null +++ b/renku/core/lfs.py @@ -0,0 +1,543 @@ +# Copyright Swiss Data Science Center (SDSC). A partnership between +# École Polytechnique Fédérale de Lausanne (EPFL) and +# Eidgenössische Technische Hochschule Zürich (ETHZ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Logic for handling a data storage.""" + +import functools +import itertools +import os +import re +import shlex +import tempfile +from collections import defaultdict +from pathlib import Path +from shutil import move, which +from subprocess import PIPE, STDOUT, check_output, run +from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Union + +import pathspec + +from renku.core import errors +from renku.core.config import get_value +from renku.core.constant import RENKU_LFS_IGNORE_PATH, RENKU_PROTECTED_PATHS +from renku.core.util import communication +from renku.core.util.git import get_in_submodules, run_command +from renku.core.util.os import expand_directories, parse_file_size +from renku.domain_model.project_context import project_context + +if TYPE_CHECKING: + from renku.infrastructure.repository import Repository + + +_CMD_STORAGE_INSTALL = ["git", "lfs", "install", "--local"] + +_CMD_STORAGE_TRACK = ["git", "lfs", "track", "--"] + +_CMD_STORAGE_UNTRACK = ["git", "lfs", "untrack", "--"] + +_CMD_STORAGE_CLEAN = ["git", "lfs", "clean"] + +_CMD_STORAGE_CHECKOUT = ["git", "lfs", "checkout"] + +_CMD_STORAGE_PULL = ["git", "lfs", "pull", "-I"] + +_CMD_STORAGE_MIGRATE_IMPORT = ["git", "lfs", "migrate", "import"] + +_CMD_STORAGE_MIGRATE_INFO = ["git", "lfs", "migrate", "info", "--top", "42000"] + +_CMD_STORAGE_LIST = ["git", "lfs", "ls-files", "-n"] + +_CMD_STORAGE_STATUS = ["git", "lfs", "status"] + +_LFS_HEADER = "version https://git-lfs.github.com/spec/" + + +class RenkuGitWildMatchPattern(pathspec.patterns.GitWildMatchPattern): + """Custom GitWildMatchPattern matcher.""" + + __slots__ = ("pattern",) + + def __init__(self, pattern, include=None): + """Initialize RenkuRegexPattern.""" + super().__init__(pattern, include) + self.pattern = pattern + + +pathspec.util.register_pattern("renku_gitwildmatch", RenkuGitWildMatchPattern) + + +def check_external_storage_wrapper(fn): + """Check availability of external storage on methods that need it. + + Raises: + ``errors.ExternalStorageNotInstalled``: If external storage isn't installed. + ``errors.ExternalStorageDisabled``: If external storage isn't enabled. + """ + + @functools.wraps(fn) + def wrapper(*args, **kwargs): + if not check_external_storage(): + pass + else: + return fn(*args, **kwargs) + + return wrapper + + +@functools.lru_cache +def storage_installed() -> bool: + """Verify that git-lfs is installed and on system PATH.""" + return bool(which("git-lfs")) + + +def storage_installed_locally() -> bool: + """Verify that git-lfs is installed for the project.""" + repo_config = project_context.repository.get_configuration(scope="local") + return repo_config.has_section('filter "lfs"') + + +def check_external_storage(): + """Check if repository has external storage enabled. + + Raises: + ``errors.ExternalStorageNotInstalled``: If external storage isn't installed. + ``errors.ExternalStorageDisabled``: If external storage isn't enabled. + """ + installed_locally = storage_installed_locally() + is_storage_installed = installed_locally and storage_installed() + + if project_context.external_storage_requested and not is_storage_installed: + raise errors.ExternalStorageDisabled() + + if installed_locally and not storage_installed(): + raise errors.ExternalStorageNotInstalled() + + return is_storage_installed + + +def renku_lfs_ignore() -> pathspec.PathSpec: + """Gets pathspec for files to not add to LFS.""" + ignore_path = project_context.path / RENKU_LFS_IGNORE_PATH + + if not os.path.exists(ignore_path): + return pathspec.PathSpec.from_lines("renku_gitwildmatch", RENKU_PROTECTED_PATHS) + with ignore_path.open("r") as f: + # NOTE: Append `renku_protected_paths` at the end to give it the highest priority + lines = itertools.chain(f, RENKU_PROTECTED_PATHS) + return pathspec.PathSpec.from_lines("renku_gitwildmatch", lines) + + +def get_minimum_lfs_file_size() -> int: + """The minimum size of a file in bytes to be added to lfs.""" + size = get_value("renku", "lfs_threshold") + + return parse_file_size(size) + + +def init_external_storage(force: bool = False) -> None: + """Initialize the external storage for data.""" + try: + result = run( + _CMD_STORAGE_INSTALL + (["--force"] if force else []), + stdout=PIPE, + stderr=STDOUT, + cwd=project_context.path, + text=True, + ) + + if result.returncode != 0: + raise errors.GitLFSError(f"Error executing 'git lfs install: \n {result.stdout}") + except (KeyboardInterrupt, OSError) as e: + raise errors.ParameterError(f"Couldn't run 'git lfs':\n{e}") + + +@check_external_storage_wrapper +def track_paths_in_storage(*paths: Union[Path, str]) -> Optional[List[str]]: + """Track paths in the external storage.""" + if not project_context.external_storage_requested or not check_external_storage(): + return None + + # Calculate which paths can be tracked in lfs + track_paths: List[str] = [] + attrs = project_context.repository.get_attributes(*paths) + + for path in paths: + path = Path(path) + + # Do not track symlinks in LFS + if path.is_symlink(): + continue + + # Do not add files with filter=lfs in .gitattributes + if attrs.get(str(path), {}).get("filter") == "lfs" or not (project_context.path / path).exists(): + continue + + relative_path = Path(path).relative_to(project_context.path) if path.is_absolute() else path + + if ( + path.is_dir() + and not renku_lfs_ignore().match_file(relative_path) + and not any(renku_lfs_ignore().match_tree(str(relative_path))) + ): + track_paths.append(str(path / "**")) + elif not renku_lfs_ignore().match_file(str(relative_path)): + file_size = os.path.getsize(str(os.path.relpath(project_context.path / path, os.getcwd()))) + if file_size >= get_minimum_lfs_file_size(): + track_paths.append(str(relative_path)) + + if track_paths: + try: + result = run_command( + _CMD_STORAGE_TRACK, + *track_paths, + stdout=PIPE, + stderr=STDOUT, + cwd=project_context.path, + universal_newlines=True, + ) + + if result and result.returncode != 0: + raise errors.GitLFSError(f"Error executing 'git lfs track: \n {result.stdout}") + except (KeyboardInterrupt, OSError) as e: + raise errors.ParameterError(f"Couldn't run 'git lfs':\n{e}") + + show_message = get_value("renku", "show_lfs_message") + if track_paths and (show_message is None or show_message.lower() == "true"): + files_list = "\n\t".join(track_paths) + communication.info( + f"Adding these files to Git LFS:\n\t{files_list}" + "\nTo disable this message in the future, run:\n\trenku config set show_lfs_message false" + ) + + return track_paths + + +@check_external_storage_wrapper +def untrack_paths_from_storage(*paths: Union[Path, str]) -> None: + """Untrack paths from the external storage.""" + try: + result = run_command( + _CMD_STORAGE_UNTRACK, + *paths, + stdout=PIPE, + stderr=STDOUT, + cwd=project_context.path, + universal_newlines=True, + ) + + if result and result.returncode != 0: + raise errors.GitLFSError(f"Error executing 'git lfs untrack: \n {result.stdout}") + except (KeyboardInterrupt, OSError) as e: + raise errors.ParameterError(f"Couldn't run 'git lfs':\n{e}") + + +@check_external_storage_wrapper +def list_tracked_paths() -> List[Path]: + """List paths tracked in lfs.""" + try: + files = check_output(_CMD_STORAGE_LIST, cwd=project_context.path, encoding="UTF-8") + except (KeyboardInterrupt, OSError) as e: + raise errors.ParameterError(f"Couldn't run 'git lfs ls-files':\n{e}") + files_split: List[Path] = [project_context.path / f for f in files.splitlines()] + return files_split + + +@check_external_storage_wrapper +def list_unpushed_lfs_paths(repository: "Repository") -> List[Path]: + """List paths tracked in lfs for a repository.""" + + if len(repository.remotes) < 1 or (repository.active_branch and not repository.active_branch.remote_branch): + raise errors.GitConfigurationError( + f"No git remote is configured for {project_context.path} branch " + + f"{repository.active_branch.name}." # type: ignore + + "Cleaning the storage cache would lead to a loss of data as " + + "it is not on a server. Please see " + + "https://www.atlassian.com/git/tutorials/syncing for " + + "information on how to sync with a remote." + ) + try: + status = check_output(_CMD_STORAGE_STATUS, cwd=project_context.path, encoding="UTF-8") + except (KeyboardInterrupt, OSError) as e: + raise errors.ParameterError(f"Couldn't run 'git lfs status':\n{e}") + + files = status.split("Objects to be committed:")[0].splitlines()[2:] + return [project_context.path / f.rsplit("(", 1)[0].strip() for f in files if f.strip()] + + +@check_external_storage_wrapper +def pull_paths_from_storage(repository: "Repository", *paths: Union[Path, str]): + """Pull paths from LFS.""" + project_dict = defaultdict(list) + + for path in expand_directories(paths): + sub_repository, _, path = get_in_submodules(repository, repository.head.commit, path) + try: + absolute_path = Path(path).resolve() + relative_path = absolute_path.relative_to(project_context.path) + except ValueError: # An external file + continue + + project_dict[sub_repository.path].append(shlex.quote(str(relative_path))) + + for project_path, file_paths in project_dict.items(): + result = run_command( + _CMD_STORAGE_PULL, + *file_paths, + separator=",", + cwd=project_path, + stdout=PIPE, + stderr=STDOUT, + universal_newlines=True, + ) + + if result and result.returncode != 0: + raise errors.GitLFSError(f"Cannot pull LFS objects from server:\n {result.stdout}") + + +@check_external_storage_wrapper +def clean_storage_cache(*check_paths: Union[Path, str]) -> Tuple[List[str], List[str]]: + """Remove paths from lfs cache.""" + project_dict = defaultdict(list) + repositories: Dict[Path, "Repository"] = {} + tracked_paths: Dict[Path, List[Path]] = {} + unpushed_paths: Dict[Path, List[Path]] = {} + untracked_paths: List[str] = [] + local_only_paths: List[str] = [] + + repository = project_context.repository + + for path in expand_directories(check_paths): + current_repository, _, path = get_in_submodules(repository=repository, commit=repository.head.commit, path=path) + try: + absolute_path = Path(path).resolve() + relative_path = absolute_path.relative_to(project_context.path) + except ValueError: # An external file + continue + + if project_context.path not in tracked_paths: + tracked_paths[project_context.path] = list_tracked_paths() + + if project_context.path not in unpushed_paths: + u_paths = list_unpushed_lfs_paths(current_repository) + unpushed_paths[project_context.path] = u_paths + + if absolute_path in unpushed_paths[project_context.path]: + local_only_paths.append(str(relative_path)) + elif absolute_path not in tracked_paths[project_context.path]: + untracked_paths.append(str(relative_path)) + else: + project_dict[project_context.path].append(str(relative_path)) + repositories[project_context.path] = current_repository + + for project_path, paths in project_dict.items(): + current_repository = repositories[project_path] + + for path in paths: + with open(path) as tracked_file: + try: + header = tracked_file.read(len(_LFS_HEADER)) + if header == _LFS_HEADER: + # file is not pulled + continue + except UnicodeDecodeError: + # likely a binary file, not lfs pointer file + pass + with tempfile.NamedTemporaryFile(mode="w+t", encoding="utf-8", delete=False) as tmp, open( + path, "r+t" + ) as input_file: + result = run(_CMD_STORAGE_CLEAN, cwd=project_path, stdin=input_file, stdout=tmp, text=True) + + if result.returncode != 0: + raise errors.GitLFSError(f"Error executing 'git lfs clean: \n {result.stdout}") + + tmp_path = tmp.name + move(tmp_path, path) + + # get lfs sha hash + old_pointer = current_repository.get_raw_content(path=path, revision="HEAD") + old_pointer = old_pointer.splitlines()[1] + old_pointer = old_pointer.split(" ")[1].split(":")[1] + + prefix1 = old_pointer[:2] + prefix2 = old_pointer[2:4] + + # remove from lfs cache + object_path = project_context.path / ".git" / "lfs" / "objects" / prefix1 / prefix2 / old_pointer + object_path.unlink() + + # add paths so they don't show as modified + current_repository.add(*paths) + + return untracked_paths, local_only_paths + + +@check_external_storage_wrapper +def checkout_paths_from_storage(*paths: Union[Path, str]): + """Checkout a paths from LFS.""" + result = run_command( + _CMD_STORAGE_CHECKOUT, + *paths, + cwd=project_context.path, + stdout=PIPE, + stderr=STDOUT, + universal_newlines=True, + ) + + if result and result.returncode != 0: + raise errors.GitLFSError(f"Error executing 'git lfs checkout: \n {result.stdout}") + + +def check_requires_tracking(*paths: Union[Path, str]) -> Optional[List[str]]: + """Check paths and return a list of those that must be tracked.""" + + if not project_context.external_storage_requested: + return None + + attrs = project_context.repository.get_attributes(*paths) + track_paths: List[str] = [] + + for path in paths: + absolute_path = Path(os.path.abspath(project_context.path / path)) + path = str(path) + + # Do not track symlinks in LFS + if absolute_path.is_symlink(): + continue + + # Do not add files with filter=lfs in .gitattributes + if attrs.get(path, {}).get("filter") == "lfs": + continue + + if not absolute_path.is_dir(): + if renku_lfs_ignore().match_file(path): + continue + if os.path.getsize(absolute_path) < get_minimum_lfs_file_size(): + continue + + track_paths.append(path) + + return track_paths + + +def get_lfs_migrate_filters() -> Tuple[List[str], List[str]]: + """Gets include, exclude and above filters for lfs migrate.""" + + def add_migrate_pattern(pattern, collection): + if pattern in RENKU_PROTECTED_PATHS: + return + pattern = pattern.strip() + if pattern.endswith("*"): + return + pattern = pattern.rstrip("/") + collection.append(f"{pattern}/**") + + includes = [] + excludes = [] + for p in renku_lfs_ignore().patterns: + if p.regex is None: + continue + + pattern = p.pattern.replace(os.linesep, "").replace("\n", "") + if pattern.startswith("!"): + pattern = pattern.replace("!", "", 1) + + if p.include: # File ignored by LFS + excludes.append(pattern) + add_migrate_pattern(pattern, excludes) + else: + includes.append(pattern) + add_migrate_pattern(pattern, includes) + + if excludes: + excludes = ["--exclude", ",".join(excludes)] + if includes: + includes = ["--include", ",".join(includes)] + + return includes, excludes + + +def check_lfs_migrate_info(everything: bool = False, use_size_filter: bool = True) -> List[str]: + """Return list of file groups in history should be in LFS.""" + ref = ( + ["--everything"] + if everything or not project_context.repository.active_branch + else ["--include-ref", project_context.repository.active_branch.name] + ) + + includes, excludes = get_lfs_migrate_filters() + + ignore_pointers = ["--pointers", "ignore"] + + command = _CMD_STORAGE_MIGRATE_INFO + ref + includes + excludes + + # NOTE: ``lfs migrate info`` supports ``--above`` while ``lfs migrate import`` doesn't. + if use_size_filter: + above = ["--above", str(get_minimum_lfs_file_size())] + command += above + + try: + lfs_output = run( + command + ignore_pointers, + stdout=PIPE, + stderr=STDOUT, + cwd=project_context.path, + text=True, + ) + except (KeyboardInterrupt, OSError) as e: + raise errors.GitError(f"Couldn't run 'git lfs migrate info':\n{e}") + + if lfs_output.returncode != 0: + # NOTE: try running without --pointers (old versions of git lfs) + try: + lfs_output = run(command, stdout=PIPE, stderr=STDOUT, cwd=project_context.path, text=True) + except (KeyboardInterrupt, OSError) as e: + raise errors.GitError(f"Couldn't run 'git lfs migrate info':\n{e}") + + if lfs_output.returncode != 0: + raise errors.GitLFSError(f"Error executing 'git lfs migrate info: \n {lfs_output.stdout}") + + groups: List[str] = [] + files_re = re.compile(r"(.*\s+[\d.]+\s+\S+).*") + + for line in lfs_output.stdout.split("\n"): + match = files_re.match(line) + if match: + groups.append(match.groups()[0]) + + if groups and use_size_filter: + # NOTE: Since there are some large files, remove the size filter so that users get list of all files that + # will be moved to LFS. + return check_lfs_migrate_info(everything=everything, use_size_filter=False) + + return groups + + +def migrate_files_to_lfs(paths: List[str]): + """Migrate files to Git LFS.""" + if paths: + includes: List[str] = ["--include", ",".join(paths)] + excludes: List[str] = [] + else: + includes, excludes = get_lfs_migrate_filters() + + command = _CMD_STORAGE_MIGRATE_IMPORT + includes + excludes + + try: + lfs_output = run(command, stdout=PIPE, stderr=STDOUT, cwd=project_context.path, text=True) + except (KeyboardInterrupt, OSError) as e: + raise errors.GitError(f"Couldn't run 'git lfs migrate import':\n{e}") + + if lfs_output.returncode != 0: + raise errors.GitLFSError(f"Error executing 'git lfs migrate import: \n {lfs_output.stdout}") diff --git a/renku/core/login.py b/renku/core/login.py index 090d97c5b5..900bf46336 100644 --- a/renku/core/login.py +++ b/renku/core/login.py @@ -195,7 +195,7 @@ def _set_renku_url_for_remote(repository: "Repository", remote_name: str, remote raise errors.GitError(f"Cannot change remote url for '{remote_name}' to '{new_remote_url}'") from e -def read_renku_token(endpoint: str, get_endpoint_from_remote=False) -> str: +def read_renku_token(endpoint: Optional[str], get_endpoint_from_remote=False) -> str: """Read renku token from renku config file. Args: @@ -287,3 +287,10 @@ def credentials(command: str, hostname: Optional[str]): communication.echo("username=renku") communication.echo(f"password={token}") + + +def ensure_login(): + """Ensure a user is logged in.""" + token = read_renku_token(None, True) + if not token: + raise errors.NotLoggedIn("You are not logged into to a Renku platform. Use 'renku login ' to log in.") diff --git a/renku/core/session/docker.py b/renku/core/session/docker.py index 129e9e93bb..2e0065a073 100644 --- a/renku/core/session/docker.py +++ b/renku/core/session/docker.py @@ -472,7 +472,10 @@ def session_open(self, project_name: str, session_name: Optional[str], **kwargs) def session_url(self, session_name: Optional[str]) -> Optional[str]: """Get the URL of the interactive session.""" - sessions = self.docker_client().containers.list() + try: + sessions = self.docker_client().containers.list() + except errors.DockerError: + return None default_url = get_value("interactive", "default_url") if not default_url: default_url = "/lab" diff --git a/renku/core/session/renkulab.py b/renku/core/session/renkulab.py index f9c95d1393..82f70c25a1 100644 --- a/renku/core/session/renkulab.py +++ b/renku/core/session/renkulab.py @@ -21,11 +21,13 @@ from datetime import datetime from pathlib import Path from time import monotonic, sleep -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union, cast +from renku.command.command_builder.command import inject from renku.core import errors from renku.core.config import get_value from renku.core.constant import ProviderPriority +from renku.core.interface.storage_service_gateway import IStorageService from renku.core.login import read_renku_token from renku.core.plugin import hookimpl from renku.core.session.utils import get_renku_project_name, get_renku_url @@ -261,6 +263,38 @@ def find_image(self, image_name: str, config: Optional[Dict[str, Any]]) -> bool: == 200 ) + def get_cloudstorage(self): + """Get cloudstorage configured for the project.""" + storage_service = cast(IStorageService, inject.instance(IStorageService)) + project_id = storage_service.project_id + if project_id is None: + communication.warn("Couldn't get project ID from Gitlab, skipping mounting cloudstorage") + return + + storages = storage_service.list(project_id) + + if not storages: + return [] + + storages_to_mount = [] + for storage, private_fields in storages: + if not communication.confirm(f"Do you want to mount storage '{storage.name}'({storage.storage_type})?"): + continue + if storage.private: + # check for credentials for user + private_field_names = [f["name"] for f in private_fields] + for name, value in storage.configuration.items(): + if name not in private_field_names: + continue + field = next(f for f in private_fields if f["name"] == name) + + secret = communication.prompt(f"{field['help']}\nPlease provide a value for secret '{name}'") + storage.configuration[name] = secret + + storages_to_mount.append({"storage_id": storage.storage_id, "configuration": storage.configuration}) + + return storages_to_mount + @hookimpl def session_provider(self) -> IHibernatingSessionProvider: """Supported session provider. @@ -373,6 +407,7 @@ def session_start( "commit_sha": session_commit, "serverOptions": server_options, "branch": repository.active_branch.name if repository.active_branch else "master", + "cloudstorage": self.get_cloudstorage(), **self._get_renku_project_name_parts(), } res = self._send_renku_request( diff --git a/renku/core/storage.py b/renku/core/storage.py index c42df05e2f..25a9946282 100644 --- a/renku/core/storage.py +++ b/renku/core/storage.py @@ -13,531 +13,20 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""Logic for handling a data storage.""" +"""Functionality for interacting with cloud storage.""" -import functools -import itertools -import os -import re -import shlex -import tempfile -from collections import defaultdict -from pathlib import Path -from shutil import move, which -from subprocess import PIPE, STDOUT, check_output, run -from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Union +from pydantic import ConfigDict, validate_call -import pathspec +from renku.command.command_builder import inject +from renku.core.interface.storage_service_gateway import IStorageService -from renku.core import errors -from renku.core.config import get_value -from renku.core.constant import RENKU_LFS_IGNORE_PATH, RENKU_PROTECTED_PATHS -from renku.core.util import communication -from renku.core.util.git import get_in_submodules, run_command -from renku.core.util.os import expand_directories, parse_file_size -from renku.domain_model.project_context import project_context -if TYPE_CHECKING: - from renku.infrastructure.repository import Repository - - -_CMD_STORAGE_INSTALL = ["git", "lfs", "install", "--local"] - -_CMD_STORAGE_TRACK = ["git", "lfs", "track", "--"] - -_CMD_STORAGE_UNTRACK = ["git", "lfs", "untrack", "--"] - -_CMD_STORAGE_CLEAN = ["git", "lfs", "clean"] - -_CMD_STORAGE_CHECKOUT = ["git", "lfs", "checkout"] - -_CMD_STORAGE_PULL = ["git", "lfs", "pull", "-I"] - -_CMD_STORAGE_MIGRATE_IMPORT = ["git", "lfs", "migrate", "import"] - -_CMD_STORAGE_MIGRATE_INFO = ["git", "lfs", "migrate", "info", "--top", "42000"] - -_CMD_STORAGE_LIST = ["git", "lfs", "ls-files", "-n"] - -_CMD_STORAGE_STATUS = ["git", "lfs", "status"] - -_LFS_HEADER = "version https://git-lfs.github.com/spec/" - - -class RenkuGitWildMatchPattern(pathspec.patterns.GitWildMatchPattern): - """Custom GitWildMatchPattern matcher.""" - - __slots__ = ("pattern",) - - def __init__(self, pattern, include=None): - """Initialize RenkuRegexPattern.""" - super().__init__(pattern, include) - self.pattern = pattern - - -pathspec.util.register_pattern("renku_gitwildmatch", RenkuGitWildMatchPattern) - - -def check_external_storage_wrapper(fn): - """Check availability of external storage on methods that need it. - - Raises: - ``errors.ExternalStorageNotInstalled``: If external storage isn't installed. - ``errors.ExternalStorageDisabled``: If external storage isn't enabled. - """ - - @functools.wraps(fn) - def wrapper(*args, **kwargs): - if not check_external_storage(): - pass - else: - return fn(*args, **kwargs) - - return wrapper - - -@functools.lru_cache -def storage_installed() -> bool: - """Verify that git-lfs is installed and on system PATH.""" - return bool(which("git-lfs")) - - -def storage_installed_locally() -> bool: - """Verify that git-lfs is installed for the project.""" - repo_config = project_context.repository.get_configuration(scope="local") - return repo_config.has_section('filter "lfs"') - - -def check_external_storage(): - """Check if repository has external storage enabled. - - Raises: - ``errors.ExternalStorageNotInstalled``: If external storage isn't installed. - ``errors.ExternalStorageDisabled``: If external storage isn't enabled. - """ - installed_locally = storage_installed_locally() - is_storage_installed = installed_locally and storage_installed() - - if project_context.external_storage_requested and not is_storage_installed: - raise errors.ExternalStorageDisabled() - - if installed_locally and not storage_installed(): - raise errors.ExternalStorageNotInstalled() - - return is_storage_installed - - -def renku_lfs_ignore() -> pathspec.PathSpec: - """Gets pathspec for files to not add to LFS.""" - ignore_path = project_context.path / RENKU_LFS_IGNORE_PATH - - if not os.path.exists(ignore_path): - return pathspec.PathSpec.from_lines("renku_gitwildmatch", RENKU_PROTECTED_PATHS) - with ignore_path.open("r") as f: - # NOTE: Append `renku_protected_paths` at the end to give it the highest priority - lines = itertools.chain(f, RENKU_PROTECTED_PATHS) - return pathspec.PathSpec.from_lines("renku_gitwildmatch", lines) - - -def get_minimum_lfs_file_size() -> int: - """The minimum size of a file in bytes to be added to lfs.""" - size = get_value("renku", "lfs_threshold") - - return parse_file_size(size) - - -def init_external_storage(force: bool = False) -> None: - """Initialize the external storage for data.""" - try: - result = run( - _CMD_STORAGE_INSTALL + (["--force"] if force else []), - stdout=PIPE, - stderr=STDOUT, - cwd=project_context.path, - text=True, - ) - - if result.returncode != 0: - raise errors.GitLFSError(f"Error executing 'git lfs install: \n {result.stdout}") - except (KeyboardInterrupt, OSError) as e: - raise errors.ParameterError(f"Couldn't run 'git lfs':\n{e}") - - -@check_external_storage_wrapper -def track_paths_in_storage(*paths: Union[Path, str]) -> Optional[List[str]]: - """Track paths in the external storage.""" - if not project_context.external_storage_requested or not check_external_storage(): - return None - - # Calculate which paths can be tracked in lfs - track_paths: List[str] = [] - attrs = project_context.repository.get_attributes(*paths) - - for path in paths: - path = Path(path) - - # Do not track symlinks in LFS - if path.is_symlink(): - continue - - # Do not add files with filter=lfs in .gitattributes - if attrs.get(str(path), {}).get("filter") == "lfs" or not (project_context.path / path).exists(): - continue - - relative_path = Path(path).relative_to(project_context.path) if path.is_absolute() else path - - if ( - path.is_dir() - and not renku_lfs_ignore().match_file(relative_path) - and not any(renku_lfs_ignore().match_tree(str(relative_path))) - ): - track_paths.append(str(path / "**")) - elif not renku_lfs_ignore().match_file(str(relative_path)): - file_size = os.path.getsize(str(os.path.relpath(project_context.path / path, os.getcwd()))) - if file_size >= get_minimum_lfs_file_size(): - track_paths.append(str(relative_path)) - - if track_paths: - try: - result = run_command( - _CMD_STORAGE_TRACK, - *track_paths, - stdout=PIPE, - stderr=STDOUT, - cwd=project_context.path, - universal_newlines=True, - ) - - if result and result.returncode != 0: - raise errors.GitLFSError(f"Error executing 'git lfs track: \n {result.stdout}") - except (KeyboardInterrupt, OSError) as e: - raise errors.ParameterError(f"Couldn't run 'git lfs':\n{e}") - - show_message = get_value("renku", "show_lfs_message") - if track_paths and (show_message is None or show_message.lower() == "true"): - files_list = "\n\t".join(track_paths) - communication.info( - f"Adding these files to Git LFS:\n\t{files_list}" - "\nTo disable this message in the future, run:\n\trenku config set show_lfs_message false" - ) - - return track_paths - - -@check_external_storage_wrapper -def untrack_paths_from_storage(*paths: Union[Path, str]) -> None: - """Untrack paths from the external storage.""" - try: - result = run_command( - _CMD_STORAGE_UNTRACK, - *paths, - stdout=PIPE, - stderr=STDOUT, - cwd=project_context.path, - universal_newlines=True, - ) - - if result and result.returncode != 0: - raise errors.GitLFSError(f"Error executing 'git lfs untrack: \n {result.stdout}") - except (KeyboardInterrupt, OSError) as e: - raise errors.ParameterError(f"Couldn't run 'git lfs':\n{e}") - - -@check_external_storage_wrapper -def list_tracked_paths() -> List[Path]: - """List paths tracked in lfs.""" - try: - files = check_output(_CMD_STORAGE_LIST, cwd=project_context.path, encoding="UTF-8") - except (KeyboardInterrupt, OSError) as e: - raise errors.ParameterError(f"Couldn't run 'git lfs ls-files':\n{e}") - files_split: List[Path] = [project_context.path / f for f in files.splitlines()] - return files_split - - -@check_external_storage_wrapper -def list_unpushed_lfs_paths(repository: "Repository") -> List[Path]: - """List paths tracked in lfs for a repository.""" - - if len(repository.remotes) < 1 or (repository.active_branch and not repository.active_branch.remote_branch): - raise errors.GitConfigurationError( - f"No git remote is configured for {project_context.path} branch " - + f"{repository.active_branch.name}." # type: ignore - + "Cleaning the storage cache would lead to a loss of data as " - + "it is not on a server. Please see " - + "https://www.atlassian.com/git/tutorials/syncing for " - + "information on how to sync with a remote." - ) - try: - status = check_output(_CMD_STORAGE_STATUS, cwd=project_context.path, encoding="UTF-8") - except (KeyboardInterrupt, OSError) as e: - raise errors.ParameterError(f"Couldn't run 'git lfs status':\n{e}") - - files = status.split("Objects to be committed:")[0].splitlines()[2:] - return [project_context.path / f.rsplit("(", 1)[0].strip() for f in files if f.strip()] - - -@check_external_storage_wrapper -def pull_paths_from_storage(repository: "Repository", *paths: Union[Path, str]): - """Pull paths from LFS.""" - project_dict = defaultdict(list) - - for path in expand_directories(paths): - sub_repository, _, path = get_in_submodules(repository, repository.head.commit, path) - try: - absolute_path = Path(path).resolve() - relative_path = absolute_path.relative_to(project_context.path) - except ValueError: # An external file - continue - - project_dict[sub_repository.path].append(shlex.quote(str(relative_path))) - - for project_path, file_paths in project_dict.items(): - result = run_command( - _CMD_STORAGE_PULL, - *file_paths, - separator=",", - cwd=project_path, - stdout=PIPE, - stderr=STDOUT, - universal_newlines=True, - ) - - if result and result.returncode != 0: - raise errors.GitLFSError(f"Cannot pull LFS objects from server:\n {result.stdout}") - - -@check_external_storage_wrapper -def clean_storage_cache(*check_paths: Union[Path, str]) -> Tuple[List[str], List[str]]: - """Remove paths from lfs cache.""" - project_dict = defaultdict(list) - repositories: Dict[Path, "Repository"] = {} - tracked_paths: Dict[Path, List[Path]] = {} - unpushed_paths: Dict[Path, List[Path]] = {} - untracked_paths: List[str] = [] - local_only_paths: List[str] = [] - - repository = project_context.repository - - for path in expand_directories(check_paths): - current_repository, _, path = get_in_submodules(repository=repository, commit=repository.head.commit, path=path) - try: - absolute_path = Path(path).resolve() - relative_path = absolute_path.relative_to(project_context.path) - except ValueError: # An external file - continue - - if project_context.path not in tracked_paths: - tracked_paths[project_context.path] = list_tracked_paths() - - if project_context.path not in unpushed_paths: - u_paths = list_unpushed_lfs_paths(current_repository) - unpushed_paths[project_context.path] = u_paths - - if absolute_path in unpushed_paths[project_context.path]: - local_only_paths.append(str(relative_path)) - elif absolute_path not in tracked_paths[project_context.path]: - untracked_paths.append(str(relative_path)) - else: - project_dict[project_context.path].append(str(relative_path)) - repositories[project_context.path] = current_repository - - for project_path, paths in project_dict.items(): - current_repository = repositories[project_path] - - for path in paths: - with open(path) as tracked_file: - try: - header = tracked_file.read(len(_LFS_HEADER)) - if header == _LFS_HEADER: - # file is not pulled - continue - except UnicodeDecodeError: - # likely a binary file, not lfs pointer file - pass - with tempfile.NamedTemporaryFile(mode="w+t", encoding="utf-8", delete=False) as tmp, open( - path, "r+t" - ) as input_file: - result = run(_CMD_STORAGE_CLEAN, cwd=project_path, stdin=input_file, stdout=tmp, text=True) - - if result.returncode != 0: - raise errors.GitLFSError(f"Error executing 'git lfs clean: \n {result.stdout}") - - tmp_path = tmp.name - move(tmp_path, path) - - # get lfs sha hash - old_pointer = current_repository.get_raw_content(path=path, revision="HEAD") - old_pointer = old_pointer.splitlines()[1] - old_pointer = old_pointer.split(" ")[1].split(":")[1] - - prefix1 = old_pointer[:2] - prefix2 = old_pointer[2:4] - - # remove from lfs cache - object_path = project_context.path / ".git" / "lfs" / "objects" / prefix1 / prefix2 / old_pointer - object_path.unlink() - - # add paths so they don't show as modified - current_repository.add(*paths) - - return untracked_paths, local_only_paths - - -@check_external_storage_wrapper -def checkout_paths_from_storage(*paths: Union[Path, str]): - """Checkout a paths from LFS.""" - result = run_command( - _CMD_STORAGE_CHECKOUT, - *paths, - cwd=project_context.path, - stdout=PIPE, - stderr=STDOUT, - universal_newlines=True, - ) - - if result and result.returncode != 0: - raise errors.GitLFSError(f"Error executing 'git lfs checkout: \n {result.stdout}") - - -def check_requires_tracking(*paths: Union[Path, str]) -> Optional[List[str]]: - """Check paths and return a list of those that must be tracked.""" - - if not project_context.external_storage_requested: - return None - - attrs = project_context.repository.get_attributes(*paths) - track_paths: List[str] = [] - - for path in paths: - absolute_path = Path(os.path.abspath(project_context.path / path)) - path = str(path) - - # Do not track symlinks in LFS - if absolute_path.is_symlink(): - continue - - # Do not add files with filter=lfs in .gitattributes - if attrs.get(path, {}).get("filter") == "lfs": - continue - - if not absolute_path.is_dir(): - if renku_lfs_ignore().match_file(path): - continue - if os.path.getsize(absolute_path) < get_minimum_lfs_file_size(): - continue - - track_paths.append(path) - - return track_paths - - -def get_lfs_migrate_filters() -> Tuple[List[str], List[str]]: - """Gets include, exclude and above filters for lfs migrate.""" - - def add_migrate_pattern(pattern, collection): - if pattern in RENKU_PROTECTED_PATHS: - return - pattern = pattern.strip() - if pattern.endswith("*"): - return - pattern = pattern.rstrip("/") - collection.append(f"{pattern}/**") - - includes = [] - excludes = [] - for p in renku_lfs_ignore().patterns: - if p.regex is None: - continue - - pattern = p.pattern.replace(os.linesep, "").replace("\n", "") - if pattern.startswith("!"): - pattern = pattern.replace("!", "", 1) - - if p.include: # File ignored by LFS - excludes.append(pattern) - add_migrate_pattern(pattern, excludes) - else: - includes.append(pattern) - add_migrate_pattern(pattern, includes) - - if excludes: - excludes = ["--exclude", ",".join(excludes)] - if includes: - includes = ["--include", ",".join(includes)] - - return includes, excludes - - -def check_lfs_migrate_info(everything: bool = False, use_size_filter: bool = True) -> List[str]: - """Return list of file groups in history should be in LFS.""" - ref = ( - ["--everything"] - if everything or not project_context.repository.active_branch - else ["--include-ref", project_context.repository.active_branch.name] - ) - - includes, excludes = get_lfs_migrate_filters() - - ignore_pointers = ["--pointers", "ignore"] - - command = _CMD_STORAGE_MIGRATE_INFO + ref + includes + excludes - - # NOTE: ``lfs migrate info`` supports ``--above`` while ``lfs migrate import`` doesn't. - if use_size_filter: - above = ["--above", str(get_minimum_lfs_file_size())] - command += above - - try: - lfs_output = run( - command + ignore_pointers, - stdout=PIPE, - stderr=STDOUT, - cwd=project_context.path, - text=True, - ) - except (KeyboardInterrupt, OSError) as e: - raise errors.GitError(f"Couldn't run 'git lfs migrate info':\n{e}") - - if lfs_output.returncode != 0: - # NOTE: try running without --pointers (old versions of git lfs) - try: - lfs_output = run(command, stdout=PIPE, stderr=STDOUT, cwd=project_context.path, text=True) - except (KeyboardInterrupt, OSError) as e: - raise errors.GitError(f"Couldn't run 'git lfs migrate info':\n{e}") - - if lfs_output.returncode != 0: - raise errors.GitLFSError(f"Error executing 'git lfs migrate info: \n {lfs_output.stdout}") - - groups: List[str] = [] - files_re = re.compile(r"(.*\s+[\d.]+\s+\S+).*") - - for line in lfs_output.stdout.split("\n"): - match = files_re.match(line) - if match: - groups.append(match.groups()[0]) - - if groups and use_size_filter: - # NOTE: Since there are some large files, remove the size filter so that users get list of all files that - # will be moved to LFS. - return check_lfs_migrate_info(everything=everything, use_size_filter=False) - - return groups - - -def migrate_files_to_lfs(paths: List[str]): - """Migrate files to Git LFS.""" - if paths: - includes: List[str] = ["--include", ",".join(paths)] - excludes: List[str] = [] - else: - includes, excludes = get_lfs_migrate_filters() - - command = _CMD_STORAGE_MIGRATE_IMPORT + includes + excludes - - try: - lfs_output = run(command, stdout=PIPE, stderr=STDOUT, cwd=project_context.path, text=True) - except (KeyboardInterrupt, OSError) as e: - raise errors.GitError(f"Couldn't run 'git lfs migrate import':\n{e}") - - if lfs_output.returncode != 0: - raise errors.GitLFSError(f"Error executing 'git lfs migrate import: \n {lfs_output.stdout}") +@inject.autoparams() +@validate_call(config=ConfigDict(arbitrary_types_allowed=True)) +def list_storage(storage_service: IStorageService): + """List configured cloud storage for project.""" + project_id = storage_service.project_id + if project_id is None: + return [] + storages = storage_service.list(project_id) + return storages diff --git a/renku/core/workflow/execute.py b/renku/core/workflow/execute.py index 494d48e09a..c9b3c7fc02 100644 --- a/renku/core/workflow/execute.py +++ b/renku/core/workflow/execute.py @@ -27,8 +27,8 @@ from renku.core import errors from renku.core.interface.activity_gateway import IActivityGateway from renku.core.interface.plan_gateway import IPlanGateway +from renku.core.lfs import check_external_storage, pull_paths_from_storage from renku.core.plugin.provider import execute -from renku.core.storage import check_external_storage, pull_paths_from_storage from renku.core.util import communication from renku.core.util.datetime8601 import local_now from renku.core.util.os import is_subpath, safe_read_yaml diff --git a/renku/core/workflow/plan_factory.py b/renku/core/workflow/plan_factory.py index 0013f683cf..1dcc59e42b 100644 --- a/renku/core/workflow/plan_factory.py +++ b/renku/core/workflow/plan_factory.py @@ -32,8 +32,8 @@ from renku.core import errors from renku.core.constant import RENKU_HOME, RENKU_TMP from renku.core.interface.project_gateway import IProjectGateway +from renku.core.lfs import check_external_storage, track_paths_in_storage from renku.core.plugin.pluginmanager import get_plugin_manager -from renku.core.storage import check_external_storage, track_paths_in_storage from renku.core.util.git import is_path_safe from renku.core.util.metadata import is_external_file from renku.core.util.os import get_absolute_path, get_relative_path, is_subpath diff --git a/renku/core/workflow/run.py b/renku/core/workflow/run.py index dbb382607c..d3af291ac7 100644 --- a/renku/core/workflow/run.py +++ b/renku/core/workflow/run.py @@ -33,7 +33,7 @@ from renku.core.git import get_mapped_std_streams from renku.core.interface.activity_gateway import IActivityGateway from renku.core.interface.plan_gateway import IPlanGateway -from renku.core.storage import check_external_storage, pull_paths_from_storage +from renku.core.lfs import check_external_storage, pull_paths_from_storage from renku.core.util.datetime8601 import local_now from renku.core.util.git import get_git_user from renku.core.util.os import get_relative_path_to_cwd, get_relative_paths diff --git a/renku/domain_model/cloud_storage.py b/renku/domain_model/cloud_storage.py new file mode 100644 index 0000000000..bccc73f2ee --- /dev/null +++ b/renku/domain_model/cloud_storage.py @@ -0,0 +1,61 @@ +# Copyright Swiss Data Science Center (SDSC) +# A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and +# Eidgenössische Technische Hochschule Zürich (ETHZ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Domain model for cloud storage.""" +from dataclasses import dataclass +from typing import Any, Dict, List, NamedTuple, Optional + + +@dataclass +class CloudStorage: + """A cloud storage definition. + + Cloud storages are defined on the storage service to easily reuse storage configurations (RClone) in projects. + """ + + name: str + source_path: str + target_path: str + configuration: Dict[str, Any] + storage_id: Optional[str] = None + project_id: Optional[str] = None + _storage_type: Optional[str] = None + + @property + def storage_type(self) -> str: + """The type of storage e.g. S3.""" + return self._storage_type or self.configuration["type"] + + @property + def private(self) -> bool: + """Whether the storage needs credentials or not.""" + return any(v == "" for _, v in self.configuration.items()) + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "CloudStorage": + """Instantiate from a dict.""" + return CloudStorage( + storage_id=data["storage_id"], + name=data["name"], + source_path=data["source_path"], + target_path=data["target_path"], + configuration=data["configuration"], + project_id=data.get("project_id"), + ) + + +CloudStorageWithSensitiveFields = NamedTuple( + "CloudStorageWithSensitiveFields", [("storage", CloudStorage), ("private_fields", List[Dict[str, Any]])] +) diff --git a/renku/ui/service/gateways/gitlab_api_provider.py b/renku/infrastructure/gitlab_api_provider.py similarity index 81% rename from renku/ui/service/gateways/gitlab_api_provider.py rename to renku/infrastructure/gitlab_api_provider.py index eac4b6a511..080e38fc18 100644 --- a/renku/ui/service/gateways/gitlab_api_provider.py +++ b/renku/infrastructure/gitlab_api_provider.py @@ -23,9 +23,9 @@ import gitlab from renku.core import errors +from renku.core.interface.git_api_provider import IGitAPIProvider from renku.core.util.os import delete_dataset_file from renku.domain_model.git import GitURL -from renku.ui.service.interfaces.git_api_provider import IGitAPIProvider from renku.ui.service.logger import service_log @@ -43,13 +43,16 @@ class GitlabAPIProvider(IGitAPIProvider): errors.AuthenticationError: If the bearer token is invalid in any way. """ + def __init__(self, token: str): + """Init gitlab provider.""" + self.token = token + def download_files_from_api( self, files: List[Union[Path, str]], folders: List[Union[Path, str]], target_folder: Union[Path, str], remote: str, - token: str, branch: Optional[str] = None, ): """Download files through a remote Git API. @@ -59,7 +62,6 @@ def download_files_from_api( folders(List[Union[Path, str]]): Folders to download. target_folder(Union[Path, str]): Destination to save downloads to. remote(str): Git remote URL. - token(str): Gitlab API token. branch(Optional[str]): Git reference (Default value = None). """ if not branch: @@ -68,30 +70,11 @@ def download_files_from_api( target_folder = Path(target_folder) git_data = GitURL.parse(remote) - try: - gl = gitlab.Gitlab(git_data.instance_url, oauth_token=token) - project = gl.projects.get(f"{git_data.owner}/{git_data.name}") - except gitlab.GitlabAuthenticationError: - # NOTE: Invalid or expired tokens fail even on public projects. Let's give it a try without tokens - try: - gl = gitlab.Gitlab(git_data.instance_url) - project = gl.projects.get(f"{git_data.owner}/{git_data.name}") - except gitlab.GitlabAuthenticationError as e: - raise errors.AuthenticationError from e - except gitlab.GitlabGetError as e: - # NOTE: better to re-raise this as a core error since it's a common case - service_log.warn(f"fast project clone didn't work: {e}", exc_info=e) - if "project not found" in getattr(e, "error_message", "").lower(): - raise errors.ProjectNotFound from e - else: - raise - except gitlab.GitlabGetError as e: - # NOTE: better to re-raise this as a core error since it's a common case - service_log.warn(f"fast project clone didn't work: {e}", exc_info=e) - if "project not found" in getattr(e, "error_message", "").lower(): - raise errors.ProjectNotFound from e - else: - raise + + if git_data.name is None: + raise errors.InvalidGitURL("Couldn't parse repo name from git url") + + project = self._get_project(git_data.instance_url, git_data.owner, git_data.name) for file in files: full_path = target_folder / file @@ -113,6 +96,41 @@ def download_files_from_api( with tarfile.open(fileobj=f) as archive: archive.extractall(path=target_folder, members=tar_members_without_top_folder(archive, 1)) + def get_project_id(self, gitlab_url: str, namespace: str, name: str) -> Optional[str]: + """Get a gitlab project id from namespace/name.""" + project = self._get_project(gitlab_url, namespace, name) + if not project: + return None + return project.id + + def _get_project(self, gitlab_url: str, namespace: str, name: str): + """Get a gitlab project.""" + try: + gl = gitlab.Gitlab(gitlab_url, oauth_token=self.token) + project = gl.projects.get(f"{namespace}/{name}") + except gitlab.GitlabAuthenticationError: + # NOTE: Invalid or expired tokens fail even on public projects. Let's give it a try without tokens + try: + gl = gitlab.Gitlab(gitlab_url) + project = gl.projects.get(f"{namespace}/{name}") + except gitlab.GitlabAuthenticationError as e: + raise errors.AuthenticationError from e + except gitlab.GitlabGetError as e: + # NOTE: better to re-raise this as a core error since it's a common case + service_log.warn(f"fast project clone didn't work: {e}", exc_info=e) + if "project not found" in getattr(e, "error_message", "").lower(): + raise errors.ProjectNotFound from e + else: + raise + except gitlab.GitlabGetError as e: + # NOTE: better to re-raise this as a core error since it's a common case + service_log.warn(f"fast project clone didn't work: {e}", exc_info=e) + if "project not found" in getattr(e, "error_message", "").lower(): + raise errors.ProjectNotFound from e + else: + raise + return project + def tar_members_without_top_folder(tar: tarfile.TarFile, strip: int) -> Generator[tarfile.TarInfo, None, None]: """Gets tar members, ignoring the top folder.""" diff --git a/renku/infrastructure/storage/storage_service.py b/renku/infrastructure/storage/storage_service.py new file mode 100644 index 0000000000..92c8ebe092 --- /dev/null +++ b/renku/infrastructure/storage/storage_service.py @@ -0,0 +1,154 @@ +# Copyright Swiss Data Science Center (SDSC) +# A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and +# Eidgenössische Technische Hochschule Zürich (ETHZ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Gateway for storage service.""" +from dataclasses import asdict +from functools import cached_property +from typing import Any, Callable, Dict, List, Optional + +import requests + +from renku.command.command_builder.command import inject +from renku.core import errors +from renku.core.interface.git_api_provider import IGitAPIProvider +from renku.core.interface.storage_service_gateway import IStorageService +from renku.core.login import read_renku_token +from renku.core.session.utils import get_renku_url +from renku.domain_model.cloud_storage import CloudStorage, CloudStorageWithSensitiveFields +from renku.domain_model.project import Project +from renku.domain_model.project_context import project_context + +TIMEOUT = 5 + + +class StorageService(IStorageService): + """Storage service gateway.""" + + base_url: str + _gl: IGitAPIProvider = inject.attr(IGitAPIProvider) + + def __init__(self): + """Create an instance.""" + renku_url = get_renku_url() + if not renku_url: + raise errors.RenkulabSessionGetUrlError() + self.base_url = f"{renku_url}api/data" + + @cached_property + def project_id(self) -> Optional[str]: + """Get the current gitlab project id. + + Note: This is mostly a workaround since storage service is already done to only accept + project ids, but the CLI knows nothing about those. + This could should be removed once we move to proper renku projects. + """ + namespace, name = Project.get_namespace_and_name( + remote=project_context.remote, name=project_context.project.name, repository=project_context.repository + ) + + if namespace is None or name is None: + raise errors.ParameterError("Couldn't get namespace or name for current project") + if namespace.startswith("repos/"): + namespace = namespace[6:] + gitlab_url = f"https://{project_context.remote.host}/repos/" + + return self._gl.get_project_id(gitlab_url, namespace, name) + + def _auth_headers(self) -> Dict[str, Any]: + """Send a request with authentication headers.""" + token = read_renku_token(None, get_endpoint_from_remote=True) + if not token: + raise errors.NotLoggedIn("Must be logged in to get access storage for a project.") + + return {"Authorization": f"Bearer {token}"} + + def _send_request( + self, + path: str, + parameters: Optional[Dict[str, Any]] = None, + body: Optional[Dict[str, Any]] = None, + method="GET", + auth=False, + expected_response=[200], + ): + """Send an unauthenticated request.""" + request_method: Callable[..., Any] + if method == "GET": + request_method = requests.get + elif method == "POST": + request_method = requests.post + elif method == "PUT": + request_method = requests.put + elif method == "DELETE": + request_method = requests.delete + else: + raise NotImplementedError() + + url = f"{self.base_url}{path}" + headers = None + + if auth: + headers = self._auth_headers() + + resp = request_method(url, headers=headers, params=parameters, data=body, timeout=TIMEOUT) # type: ignore + + if resp.status_code not in expected_response: + raise errors.RequestError(f"Request to storage service failed ({resp.status_code}): {resp.text}") + + return resp.json() + + def list(self, project_id: str) -> List[CloudStorageWithSensitiveFields]: + """List storage configured for the current project.""" + response = self._send_request("/storage", parameters={"project_id": project_id}, auth=True) + results = [] + for res in response: + results.append( + CloudStorageWithSensitiveFields(CloudStorage.from_dict(res["storage"]), res["sensitive_fields"]) + ) + + return results + + def create(self, storage: CloudStorage) -> CloudStorageWithSensitiveFields: + """Create a new cloud storage.""" + if storage.storage_id is not None: + raise ValueError("Cannot create storage with 'storage_id' already set.") + if storage.project_id is None: + raise ValueError("'project_id' must be set when creating CloudStorage.") + response = self._send_request( + "/storage", body=asdict(storage), method="POST", auth=True, expected_response=[201] + ) + return CloudStorageWithSensitiveFields( + CloudStorage.from_dict(response["storage"]), response["sensitive_fields"] + ) + + def edit(self, storage_id: str, new_storage: CloudStorage) -> CloudStorageWithSensitiveFields: + """Edit a cloud storage.""" + response = self._send_request(f"/storage/{storage_id}", body=asdict(new_storage), method="PUT", auth=True) + return CloudStorageWithSensitiveFields( + CloudStorage.from_dict(response["storage"]), response["sensitive_fields"] + ) + + def delete(self, storage_id: str) -> None: + """Delete a cloud storage.""" + self._send_request(f"/storage{storage_id}", method="DELETE", auth=True, expected_response=[204]) + + def validate(self, storage: CloudStorage) -> None: + """Validate a cloud storage. + + Raises an exception for invalid storage. + """ + self._send_request( + "/storage_schema/validate", body=storage.configuration, method="POST", expected_response=[204] + ) diff --git a/renku/ui/cli/__init__.py b/renku/ui/cli/__init__.py index 3effa698bd..217738e39e 100644 --- a/renku/ui/cli/__init__.py +++ b/renku/ui/cli/__init__.py @@ -102,6 +102,7 @@ from renku.ui.cli.githooks import githooks as githooks_command from renku.ui.cli.graph import graph from renku.ui.cli.init import init +from renku.ui.cli.lfs import lfs from renku.ui.cli.log import log from renku.ui.cli.login import credentials, login, logout from renku.ui.cli.mergetool import mergetool @@ -257,6 +258,7 @@ def help(ctx): cli.add_command(githooks_command) cli.add_command(graph) cli.add_command(init) +cli.add_command(lfs) cli.add_command(log) cli.add_command(login) cli.add_command(logout) diff --git a/renku/ui/cli/lfs.py b/renku/ui/cli/lfs.py new file mode 100644 index 0000000000..9070055c6f --- /dev/null +++ b/renku/ui/cli/lfs.py @@ -0,0 +1,175 @@ +# Copyright Swiss Data Science Center (SDSC) +# A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and +# Eidgenössische Technische Hochschule Zürich (ETHZ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r"""Manage an external storage. + +Commands and options +~~~~~~~~~~~~~~~~~~~~ + +.. rst-class:: cli-reference-commands + +.. click:: renku.ui.cli.lfs:lfs + :prog: renku lfs + :nested: full + +Pulling files from git LFS +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +LFS works by checking small pointer files into git and saving the actual +contents of a file in LFS. If instead of your file content, you see +something like this, it means the file is stored in git LFS and its +contents are not currently available locally (they are not pulled): + +.. code-block:: console + + version https://git-lfs.github.com/spec/v1 + oid sha256:42b5c7fb2acd54f6d3cd930f18fee3bdcb20598764ca93bdfb38d7989c054bcf + size 12 + +You can manually pull contents of file(s) you want with: + +.. code-block:: console + + $ renku lfs pull file1 file2 + +.. cheatsheet:: + :group: Misc + :command: $ renku lfs pull ... + :description: Pull 's from external storage (LFS). + :target: rp + +Removing local content of files stored in git LFS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you want to restore a file back to its pointer file state, for instance +to free up space locally, you can run: + +.. code-block:: console + + $ renku lfs clean file1 file2 + +This removes any data cached locally for files tracked in in git LFS. + +Migrate large files to git LFS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you accidentally checked a large file into git or are moving a non-LFS +renku repo to git LFS, you can use the following command to migrate the files +to LFS: + +.. code-block:: console + + $ renku lfs migrate --all + +This will move all files that are not excluded by `.renkulfsignore` into git +LFS. + +.. note:: + + Recent versions of Git LFS don't support filtering files based on their + size. Therefore, Renku ignores `lfs_threshold` config value when migrating + files to LFS using this command. + +To only migrate specific files, you can also pass their paths to the command +like: + +.. code-block:: console + + $ renku lfs migrate big_file other_big_file +""" +import os + +import click + +import renku.ui.cli.utils.color as color +from renku.command.util import WARNING +from renku.ui.cli.utils.callback import ClickCallback + + +@click.group() +def lfs(): + """Manage lfs.""" + + +@lfs.command() +@click.argument("paths", type=click.Path(exists=True, dir_okay=True), nargs=-1, required=True) +def pull(paths): + """Pull the specified paths from external storage.""" + from renku.command.lfs import pull_command + + pull_command().build().execute(paths=paths) + + +@lfs.command() +@click.argument("paths", type=click.Path(exists=True, dir_okay=True), nargs=-1, required=True) +def clean(paths): + """Remove files from lfs cache/turn them back into pointer files.""" + from renku.command.lfs import clean_command + + communicator = ClickCallback() + clean_command().with_communicator(communicator).build().execute(paths=paths) + + click.secho("OK", fg=color.GREEN) + + +@lfs.command("check-lfs-hook", hidden=True) +@click.argument("paths", type=click.Path(exists=True, dir_okay=True), nargs=-1, required=True) +def check_lfs_hook(paths): + """Check specified paths are tracked in external storage.""" + from renku.command.lfs import check_lfs_hook_command + + paths = check_lfs_hook_command().build().execute(paths=paths).output + if paths: + click.echo(os.linesep.join(paths)) + exit(1) + + +@lfs.command() +@click.option("--all", is_flag=True, help="Include all branches.") +def check(all): + """Check if large files are committed to Git history.""" + from renku.command.lfs import check_lfs_command + + files = check_lfs_command().build().execute(everything=all).output + if files: + message = WARNING + "Git history contains large files\n\t" + "\n\t".join(files) + click.echo(message) + exit(1) + else: + click.secho("OK", fg=color.GREEN) + + +@lfs.command() +@click.option("--all", "-a", "migrate_all", is_flag=True, default=False, help="Migrate all large files not in git LFS.") +@click.argument("paths", type=click.Path(exists=True, dir_okay=True), nargs=-1) +def migrate(migrate_all, paths): + """Migrate large files committed to git by moving them to LFS.""" + from renku.command.lfs import check_lfs_command, fix_lfs_command + + if not paths: + if not migrate_all: + click.echo("Please specify paths to migrate or use the --all flag to migrate all large files.") + exit(1) + + lfs_paths = check_lfs_command().build().execute(everything=migrate_all).output + + if not lfs_paths: + click.echo("All files are already in LFS") + exit(0) + + if not click.confirm("The following files will be moved to Git LFS:\n\t" + "\n\t".join(lfs_paths)): + exit(0) + + fix_lfs_command().build().execute(paths) diff --git a/renku/ui/cli/storage.py b/renku/ui/cli/storage.py index 00ebf40482..6e8b7b0037 100644 --- a/renku/ui/cli/storage.py +++ b/renku/ui/cli/storage.py @@ -13,7 +13,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -r"""Manage an external storage. +r"""Manage an cloud storage. Commands and options ~~~~~~~~~~~~~~~~~~~~ @@ -24,99 +24,62 @@ :prog: renku storage :nested: full -Pulling files from git LFS -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -LFS works by checking small pointer files into git and saving the actual -contents of a file in LFS. If instead of your file content, you see -something like this, it means the file is stored in git LFS and its -contents are not currently available locally (they are not pulled): - -.. code-block:: console - - version https://git-lfs.github.com/spec/v1 - oid sha256:42b5c7fb2acd54f6d3cd930f18fee3bdcb20598764ca93bdfb38d7989c054bcf - size 12 - -You can manually pull contents of file(s) you want with: - -.. code-block:: console - - $ renku storage pull file1 file2 - -.. cheatsheet:: - :group: Misc - :command: $ renku storage pull ... - :description: Pull 's from external storage (LFS). - :target: rp - -Removing local content of files stored in git LFS -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you want to restore a file back to its pointer file state, for instance -to free up space locally, you can run: - -.. code-block:: console - - $ renku storage clean file1 file2 - -This removes any data cached locally for files tracked in in git LFS. - -Migrate large files to git LFS -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you accidentally checked a large file into git or are moving a non-LFS -renku repo to git LFS, you can use the following command to migrate the files -to LFS: - -.. code-block:: console - - $ renku storage migrate --all - -This will move all files that are not excluded by `.renkulfsignore` into git -LFS. - -.. note:: - - Recent versions of Git LFS don't support filtering files based on their - size. Therefore, Renku ignores `lfs_threshold` config value when migrating - files to LFS using this command. - -To only migrate specific files, you can also pass their paths to the command -like: - -.. code-block:: console - - $ renku storage migrate big_file other_big_file """ import os import click import renku.ui.cli.utils.color as color +from renku.command.format.storage import CLOUD_STORAGE_COLUMNS, CLOUD_STORAGE_FORMATS from renku.command.util import WARNING from renku.ui.cli.utils.callback import ClickCallback @click.group() def storage(): - """Manage an external storage.""" + """Manage storage.""" @storage.command() +@click.option( + "--columns", + type=click.STRING, + default=None, + metavar="", + help="Comma-separated list of column to display: {}.".format(", ".join(CLOUD_STORAGE_COLUMNS.keys())), + show_default=True, +) +@click.option( + "--format", type=click.Choice(list(CLOUD_STORAGE_FORMATS.keys())), default="log", help="Choose an output format." +) +def ls(columns, format): + """List configured cloud storage for a project.""" + from renku.command.storage import list_storage_command + + result = list_storage_command().build().execute() + + storages = [s.storage for s in result.output] + + click.echo(CLOUD_STORAGE_FORMATS[format](storages, columns=columns)) + + +# ============================================= +# Deprecated LFS commands below, see lfs.py +# ============================================= +@storage.command(hidden=True, deprecated=True) @click.argument("paths", type=click.Path(exists=True, dir_okay=True), nargs=-1, required=True) def pull(paths): """Pull the specified paths from external storage.""" - from renku.command.storage import pull_command + from renku.command.lfs import pull_command pull_command().build().execute(paths=paths) -@storage.command() +@storage.command(hidden=True, deprecated=True) @click.argument("paths", type=click.Path(exists=True, dir_okay=True), nargs=-1, required=True) def clean(paths): """Remove files from lfs cache/turn them back into pointer files.""" - from renku.command.storage import clean_command + from renku.command.lfs import clean_command communicator = ClickCallback() clean_command().with_communicator(communicator).build().execute(paths=paths) @@ -124,11 +87,11 @@ def clean(paths): click.secho("OK", fg=color.GREEN) -@storage.command("check-lfs-hook", hidden=True) +@storage.command("check-lfs-hook", hidden=True, deprecated=True) @click.argument("paths", type=click.Path(exists=True, dir_okay=True), nargs=-1, required=True) def check_lfs_hook(paths): """Check specified paths are tracked in external storage.""" - from renku.command.storage import check_lfs_hook_command + from renku.command.lfs import check_lfs_hook_command paths = check_lfs_hook_command().build().execute(paths=paths).output if paths: @@ -136,11 +99,11 @@ def check_lfs_hook(paths): exit(1) -@storage.command() +@storage.command(hidden=True, deprecated=True) @click.option("--all", is_flag=True, help="Include all branches.") def check(all): """Check if large files are committed to Git history.""" - from renku.command.storage import check_lfs_command + from renku.command.lfs import check_lfs_command files = check_lfs_command().build().execute(everything=all).output if files: @@ -151,12 +114,12 @@ def check(all): click.secho("OK", fg=color.GREEN) -@storage.command() +@storage.command(hidden=True, deprecated=True) @click.option("--all", "-a", "migrate_all", is_flag=True, default=False, help="Migrate all large files not in git LFS.") @click.argument("paths", type=click.Path(exists=True, dir_okay=True), nargs=-1) def migrate(migrate_all, paths): """Migrate large files committed to git by moving them to LFS.""" - from renku.command.storage import check_lfs_command, fix_lfs_command + from renku.command.lfs import check_lfs_command, fix_lfs_command if not paths: if not migrate_all: diff --git a/renku/ui/service/controllers/api/mixins.py b/renku/ui/service/controllers/api/mixins.py index d1d62a61c1..0b431a28a6 100644 --- a/renku/ui/service/controllers/api/mixins.py +++ b/renku/ui/service/controllers/api/mixins.py @@ -53,7 +53,7 @@ def local_identity(method): @wraps(method) def _impl(self, *method_args, **method_kwargs): """Implementation of method wrapper.""" - if not hasattr(self, "user") and not isinstance(getattr(self, "user", None), User): + if not self.user or not isinstance(self.user, User): raise UserAnonymousError() return method(self, *method_args, **method_kwargs) @@ -82,6 +82,8 @@ def __init__( """Read operation mixin for controllers.""" if user_data and "user_id" in user_data and cache is not None: self.user = cache.ensure_user(user_data) + else: + self.user = None self.is_write = False self.migrate_project = migrate_project diff --git a/renku/ui/service/controllers/cache_migrations_check.py b/renku/ui/service/controllers/cache_migrations_check.py index 2327bf13d6..5025251278 100644 --- a/renku/ui/service/controllers/cache_migrations_check.py +++ b/renku/ui/service/controllers/cache_migrations_check.py @@ -18,13 +18,14 @@ import tempfile from dataclasses import asdict from pathlib import Path +from typing import Type from renku.command.migrate import MigrationCheckResult, migrations_check from renku.core.errors import AuthenticationError, MinimumVersionError, ProjectNotFound, RenkuException +from renku.core.interface.git_api_provider import IGitAPIProvider from renku.core.util.contexts import renku_project_context from renku.ui.service.controllers.api.abstract import ServiceCtrl from renku.ui.service.controllers.api.mixins import RenkuOperationMixin -from renku.ui.service.interfaces.git_api_provider import IGitAPIProvider from renku.ui.service.logger import service_log from renku.ui.service.serializers.cache import ProjectMigrationCheckRequest, ProjectMigrationCheckResponseRPC from renku.ui.service.views import result_response @@ -36,11 +37,13 @@ class MigrationsCheckCtrl(ServiceCtrl, RenkuOperationMixin): REQUEST_SERIALIZER = ProjectMigrationCheckRequest() RESPONSE_SERIALIZER = ProjectMigrationCheckResponseRPC() - def __init__(self, cache, user_data, request_data, git_api_provider: IGitAPIProvider): + def __init__(self, cache, user_data, request_data, git_api_provider: Type[IGitAPIProvider]): """Construct migration check controller.""" self.ctx = MigrationsCheckCtrl.REQUEST_SERIALIZER.load(request_data) - self.git_api_provider = git_api_provider super().__init__(cache, user_data, request_data) + self.git_api_provider = None + if self.user: + self.git_api_provider = git_api_provider(token=self.user.token) @property def context(self): @@ -51,8 +54,10 @@ def _fast_op_without_cache(self): """Execute renku_op with only necessary files, without cloning the whole repo.""" if "git_url" not in self.context: raise RenkuException("context does not contain `git_url`") + if not self.git_api_provider: + return None - token = self.user.token if hasattr(self, "user") else self.user_data.get("token") + token = self.user.token if self.user else self.user_data.get("token") if not token: # User isn't logged in, fast op doesn't work @@ -68,7 +73,6 @@ def _fast_op_without_cache(self): target_folder=tempdir_path, remote=self.ctx["git_url"], branch=self.request_data.get("branch", None), - token=self.user.token, ) with renku_project_context(tempdir_path): self.project_path = tempdir_path diff --git a/renku/ui/service/controllers/utils/remote_project.py b/renku/ui/service/controllers/utils/remote_project.py index f3c4e404fa..dfcd01786b 100644 --- a/renku/ui/service/controllers/utils/remote_project.py +++ b/renku/ui/service/controllers/utils/remote_project.py @@ -15,6 +15,7 @@ # limitations under the License. """Utilities for renku service controllers.""" +import os import tempfile from contextlib import contextmanager from urllib.parse import urlparse @@ -66,6 +67,7 @@ def remote(self): """Retrieve project metadata.""" with tempfile.TemporaryDirectory() as td: try: + os.environ["GIT_LFS_SKIP_SMUDGE"] = "1" clone_renku_repository( url=self.remote_url.geturl(), path=td, diff --git a/renku/ui/service/views/cache.py b/renku/ui/service/views/cache.py index aa5df0bade..ba6f24743b 100644 --- a/renku/ui/service/views/cache.py +++ b/renku/ui/service/views/cache.py @@ -16,13 +16,13 @@ """Renku service cache views.""" from flask import jsonify, request +from renku.infrastructure.gitlab_api_provider import GitlabAPIProvider from renku.ui.service.config import SERVICE_PREFIX from renku.ui.service.controllers.cache_files_delete_chunks import DeleteFileChunksCtrl from renku.ui.service.controllers.cache_files_upload import UploadFilesCtrl from renku.ui.service.controllers.cache_list_uploaded import ListUploadedFilesCtrl from renku.ui.service.controllers.cache_migrate_project import MigrateProjectCtrl from renku.ui.service.controllers.cache_migrations_check import MigrationsCheckCtrl -from renku.ui.service.gateways.gitlab_api_provider import GitlabAPIProvider from renku.ui.service.gateways.repository_cache import LocalRepositoryCache from renku.ui.service.jobs.cleanup import cache_files_cleanup from renku.ui.service.views.api_versions import ( @@ -186,7 +186,7 @@ def migration_check_project_view(user_data, cache): tags: - cache """ - return MigrationsCheckCtrl(cache, user_data, dict(request.args), GitlabAPIProvider()).to_response() + return MigrationsCheckCtrl(cache, user_data, dict(request.args), GitlabAPIProvider).to_response() @cache_blueprint.route("/cache.cleanup", methods=["GET"], provide_automatic_options=False, versions=VERSIONS_FROM_V2_1) diff --git a/renku/ui/service/views/v1/cache.py b/renku/ui/service/views/v1/cache.py index f721d60772..c16f6a30df 100644 --- a/renku/ui/service/views/v1/cache.py +++ b/renku/ui/service/views/v1/cache.py @@ -19,9 +19,9 @@ from flask import request from renku.core.errors import AuthenticationError, ProjectNotFound +from renku.infrastructure.gitlab_api_provider import GitlabAPIProvider from renku.ui.service.controllers.cache_migrate_project import MigrateProjectCtrl from renku.ui.service.controllers.cache_migrations_check import MigrationsCheckCtrl -from renku.ui.service.gateways.gitlab_api_provider import GitlabAPIProvider from renku.ui.service.serializers.v1.cache import ProjectMigrateResponseRPC_1_0, ProjectMigrationCheckResponseRPC_1_5 from renku.ui.service.views import result_response from renku.ui.service.views.api_versions import VERSIONS_BEFORE_1_1, VERSIONS_BEFORE_2_0 @@ -92,7 +92,7 @@ def migration_check_project_view_1_5(user_data, cache): from renku.ui.service.serializers.rpc import JsonRPCResponse from renku.ui.service.views.error_handlers import pretty_print_error - ctrl = MigrationsCheckCtrl(cache, user_data, dict(request.args), GitlabAPIProvider()) + ctrl = MigrationsCheckCtrl(cache, user_data, dict(request.args), GitlabAPIProvider) if "project_id" in ctrl.context: # type: ignore result = ctrl.execute_op() diff --git a/tests/cli/test_clone.py b/tests/cli/test_clone.py index 8eb55d289a..906f191e74 100644 --- a/tests/cli/test_clone.py +++ b/tests/cli/test_clone.py @@ -32,7 +32,7 @@ @pytest.mark.parametrize("url", ["https://gitlab.dev.renku.ch/renku-testing/project-9"]) def test_clone(runner, monkeypatch, url): """Test cloning of a Renku repo and existence of required settings.""" - import renku.core.storage + import renku.core.lfs with runner.isolated_filesystem() as project_path: result = runner.invoke(cli, ["clone", url, project_path]) @@ -50,7 +50,7 @@ def test_clone(runner, monkeypatch, url): # Check Git LFS is enabled with monkeypatch.context() as monkey: # Pretend that git-lfs is not installed. - monkey.setattr(renku.core.storage, "storage_installed", lambda: False) + monkey.setattr(renku.core.lfs, "storage_installed", lambda: False) # Repo is using external storage but it's not installed. result = runner.invoke(cli, ["run", "touch", "output"]) diff --git a/tests/cli/test_datasets.py b/tests/cli/test_datasets.py index e873447cc9..4c440c5d55 100644 --- a/tests/cli/test_datasets.py +++ b/tests/cli/test_datasets.py @@ -35,7 +35,7 @@ from renku.core.dataset.providers.factory import ProviderFactory from renku.core.dataset.providers.zenodo import ZenodoProvider from renku.core.interface.storage import FileHash -from renku.core.storage import track_paths_in_storage +from renku.core.lfs import track_paths_in_storage from renku.core.util.git import get_dirty_paths from renku.core.util.urls import get_slug from renku.domain_model.dataset import Dataset diff --git a/tests/cli/test_workflow.py b/tests/cli/test_workflow.py index 19d970e5e0..b58de45336 100644 --- a/tests/cli/test_workflow.py +++ b/tests/cli/test_workflow.py @@ -17,7 +17,6 @@ import datetime import itertools -import logging import os import re import shutil @@ -1282,9 +1281,8 @@ def test_workflow_cycle_detection(run_shell, project, capsys, transaction_id): @pytest.mark.skipif(sys.platform == "darwin", reason="GitHub macOS image doesn't include Docker") -def test_workflow_execute_docker_toil(runner, project, run_shell, caplog): +def test_workflow_execute_docker_toil(runner, project, run_shell): """Test workflow execute using docker with the toil provider.""" - caplog.set_level(logging.INFO) write_and_commit_file(project.repository, "input", "first line\nsecond line") output = project.path / "output" @@ -1293,13 +1291,17 @@ def test_workflow_execute_docker_toil(runner, project, run_shell, caplog): assert "first line" not in output.read_text() - write_and_commit_file(project.repository, "toil.yaml", "logLevel: INFO\ndocker:\n image: ubuntu") + log_file = tempfile.mktemp() + write_and_commit_file( + project.repository, "toil.yaml", f"logLevel: INFO\nlogFile: {log_file}\ndocker:\n image: ubuntu" + ) result = runner.invoke(cli, ["workflow", "execute", "-p", "toil", "-s", "n-1=2", "-c", "toil.yaml", "run-1"]) assert 0 == result.exit_code, format_result_exception(result) assert "first line" in output.read_text() - assert "executing with Docker" in caplog.text + # there is a bug with this currently, see issue 3652. Renable when that is fixed. + # assert "executing with Docker" in Path(log_file).read_text() def test_workflow_execute_docker_toil_stderr(runner, project, run_shell): diff --git a/tests/core/commands/test_cli.py b/tests/core/commands/test_cli.py index 4c869064ca..53386473e6 100644 --- a/tests/core/commands/test_cli.py +++ b/tests/core/commands/test_cli.py @@ -23,7 +23,7 @@ import pytest -import renku.core.storage +import renku.core.lfs from renku import __version__ from renku.core.config import get_value, load_config, remove_value, set_value, store_config from renku.core.constant import DEFAULT_DATA_DIR as DATA_DIR @@ -247,7 +247,7 @@ def test_configuration_of_no_external_storage(isolated_runner, monkeypatch, proj assert 0 == result.exit_code, format_result_exception(result) # Pretend that git-lfs is not installed. with monkeypatch.context() as monkey: - monkey.setattr(renku.core.storage, "storage_installed", lambda: False) + monkey.setattr(renku.core.lfs, "storage_installed", lambda: False) # Missing --no-external-storage flag. result = runner.invoke(cli, ["run", "touch", "output"]) assert "External storage is not configured" in result.output @@ -273,7 +273,7 @@ def test_configuration_of_external_storage(isolated_runner, monkeypatch, project assert 0 == result.exit_code, format_result_exception(result) # Pretend that git-lfs is not installed. with monkeypatch.context() as monkey: - monkey.setattr(renku.core.storage, "storage_installed", lambda: False) + monkey.setattr(renku.core.lfs, "storage_installed", lambda: False) # Repo is using external storage but it's not installed. result = runner.invoke(cli, ["run", "touch", "output"]) assert 1 == result.exit_code @@ -303,7 +303,7 @@ def test_early_check_of_external_storage(isolated_runner, monkeypatch, directory # Pretend that git-lfs is not installed. with monkeypatch.context() as monkey: - monkey.setattr(renku.core.storage, "storage_installed", lambda: False) + monkey.setattr(renku.core.lfs, "storage_installed", lambda: False) failing_command = ["dataset", "add", "--copy", "-s", "src", "my-dataset", str(directory_tree)] result = isolated_runner.invoke(cli, failing_command) @@ -362,7 +362,7 @@ def test_status_with_submodules(isolated_runner, monkeypatch, project_init): os.chdir("../foo") with monkeypatch.context() as monkey: - monkey.setattr(renku.core.storage, "storage_installed", lambda: False) + monkey.setattr(renku.core.lfs, "storage_installed", lambda: False) result = runner.invoke(cli, ["dataset", "add", "--copy", "f", "../woop"], catch_exceptions=False) diff --git a/tests/core/commands/test_doctor.py b/tests/core/commands/test_doctor.py index 5dd4c869ac..4b1f2f4fbc 100644 --- a/tests/core/commands/test_doctor.py +++ b/tests/core/commands/test_doctor.py @@ -16,7 +16,7 @@ """Renku doctor tests.""" from renku.core.constant import RENKU_LFS_IGNORE_PATH -from renku.core.storage import get_minimum_lfs_file_size +from renku.core.lfs import get_minimum_lfs_file_size from renku.domain_model.dataset import DatasetFile, Url from renku.domain_model.project_context import project_context from renku.infrastructure.gateway.activity_gateway import ActivityGateway diff --git a/tests/core/commands/test_storage.py b/tests/core/commands/test_lfs.py similarity index 99% rename from tests/core/commands/test_storage.py rename to tests/core/commands/test_lfs.py index 90075a7f39..483ad43c2f 100644 --- a/tests/core/commands/test_storage.py +++ b/tests/core/commands/test_lfs.py @@ -13,7 +13,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""Storage command tests.""" +"""LFS command tests.""" import os import subprocess diff --git a/tests/core/management/test_storage.py b/tests/core/management/test_storage.py index b4448df39c..169d079dca 100644 --- a/tests/core/management/test_storage.py +++ b/tests/core/management/test_storage.py @@ -19,7 +19,7 @@ import pytest -from renku.core.storage import get_lfs_migrate_filters, track_paths_in_storage +from renku.core.lfs import get_lfs_migrate_filters, track_paths_in_storage from renku.domain_model.project_context import project_context diff --git a/tests/core/test_storage.py b/tests/core/test_storage.py new file mode 100644 index 0000000000..6ca0eb56c3 --- /dev/null +++ b/tests/core/test_storage.py @@ -0,0 +1,56 @@ +# Copyright Swiss Data Science Center (SDSC) +# A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and +# Eidgenössische Technische Hochschule Zürich (ETHZ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Tests for storage service.""" + +from unittest.mock import MagicMock + +import renku.infrastructure.storage.storage_service as storage_service +from renku.command.command_builder.command import inject, remove_injector +from renku.core.interface.git_api_provider import IGitAPIProvider + + +def test_storage_service_list(monkeypatch): + """Test listing storage.""" + inject.configure(lambda binder: binder.bind(IGitAPIProvider, MagicMock()), bind_in_runtime=False) + + try: + with monkeypatch.context() as monkey: + + def _send_request(*_, **__): + return [ + { + "storage": { + "storage_id": "ABCDEFG", + "name": "mystorage", + "source_path": "source/path", + "target_path": "target/path", + "private": True, + "configuration": {"type": "s3", "endpoint": "example.com"}, + }, + "sensitive_fields": {}, + } + ] + + monkey.setattr(storage_service.StorageService, "_send_request", _send_request) + monkey.setattr(storage_service, "get_renku_url", lambda: "http://example.com") + svc = storage_service.StorageService() + storages = svc.list("123456") + assert len(storages) == 1 + assert storages[0].storage.name == "mystorage" + assert storages[0].storage.storage_type == "s3" + + finally: + remove_injector() diff --git a/tests/fixtures/common.py b/tests/fixtures/common.py index 6a8c77f854..4d2f2da6d9 100644 --- a/tests/fixtures/common.py +++ b/tests/fixtures/common.py @@ -22,7 +22,7 @@ import pytest from renku.core.config import set_value -from renku.core.storage import get_minimum_lfs_file_size +from renku.core.lfs import get_minimum_lfs_file_size @pytest.fixture diff --git a/tests/fixtures/storage.py b/tests/fixtures/storage.py new file mode 100644 index 0000000000..b4b222151f --- /dev/null +++ b/tests/fixtures/storage.py @@ -0,0 +1,59 @@ +# Copyright Swiss Data Science Center (SDSC) +# A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and +# Eidgenössische Technische Hochschule Zürich (ETHZ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Storage fixtures.""" +from renku.core.interface.storage_service_gateway import IStorageService +from renku.domain_model.cloud_storage import CloudStorage, CloudStorageWithSensitiveFields + + +class DummyStorageService(IStorageService): + """Dummy storage service.""" + + @property + def project_id(self): + """Get a dummy project id.""" + return "123456" + + def list(self, project_id): + """List dummy storage definition.""" + return [ + CloudStorageWithSensitiveFields( + CloudStorage( + name="mystorage", + source_path="source", + target_path="target/path", + configuration={"type": "s3", "endpoint": "example.com"}, + storage_id="ABCDEFG", + project_id="123456", + ), + [], + ) + ] + + def create(self, storage): + """Create storage.""" + raise NotImplementedError() + + def edit(self, storage_id, storage): + """Edit storage.""" + raise NotImplementedError() + + def delete(self, storage_id): + """Delete storage.""" + raise NotImplementedError() + + def validate(self, storage): + """Validate storage.""" + raise NotImplementedError() diff --git a/tests/service/controllers/utils/test_remote_project.py b/tests/service/controllers/utils/test_remote_project.py index 6e9c27339d..0a4e7a5355 100644 --- a/tests/service/controllers/utils/test_remote_project.py +++ b/tests/service/controllers/utils/test_remote_project.py @@ -20,6 +20,7 @@ import renku from renku.command.migrate import migrations_check +from renku.core.errors import MigrationRequired from renku.ui.service.controllers.utils.remote_project import RemoteProject from tests.utils import retry_failed @@ -100,6 +101,6 @@ def test_remote_project_context(): assert result.core_renku_version == renku.__version__ assert result.project_renku_version == "pre-0.11.0" assert result.core_compatibility_status.migration_required is True - assert isinstance(result.template_status, ValueError) + assert isinstance(result.template_status, MigrationRequired) assert result.dockerfile_renku_status.automated_dockerfile_update is False assert result.project_supported is True diff --git a/tests/utils.py b/tests/utils.py index 890bdf4add..9ed3e70d74 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -165,11 +165,13 @@ def get_test_bindings() -> Tuple[Dict, Dict[Type, Callable[[], Any]]]: from renku.core.interface.dataset_gateway import IDatasetGateway from renku.core.interface.plan_gateway import IPlanGateway from renku.core.interface.project_gateway import IProjectGateway + from renku.core.interface.storage_service_gateway import IStorageService from renku.infrastructure.gateway.activity_gateway import ActivityGateway from renku.infrastructure.gateway.database_gateway import DatabaseGateway from renku.infrastructure.gateway.dataset_gateway import DatasetGateway from renku.infrastructure.gateway.plan_gateway import PlanGateway from renku.infrastructure.gateway.project_gateway import ProjectGateway + from tests.fixtures.storage import DummyStorageService constructor_bindings = { IPlanGateway: lambda: PlanGateway(), @@ -177,6 +179,7 @@ def get_test_bindings() -> Tuple[Dict, Dict[Type, Callable[[], Any]]]: IDatabaseGateway: lambda: DatabaseGateway(), IDatasetGateway: lambda: DatasetGateway(), IProjectGateway: lambda: ProjectGateway(), + IStorageService: lambda: DummyStorageService(), } return {}, constructor_bindings From 398ec2ef35ec296aa55f3cdd568e35eaa360cd89 Mon Sep 17 00:00:00 2001 From: Ralf Grubenmann Date: Thu, 18 Jan 2024 18:09:31 +0100 Subject: [PATCH 13/15] fix(svc): fix migration not working with old template metadata (#3687) --- renku/core/migration/migrate.py | 7 ++++++- .../service/controllers/cache_migrate_project.py | 16 +++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/renku/core/migration/migrate.py b/renku/core/migration/migrate.py index 30e0e37a17..233cf71304 100644 --- a/renku/core/migration/migrate.py +++ b/renku/core/migration/migrate.py @@ -142,7 +142,12 @@ def migrate_project( except Exception as e: raise TemplateUpdateError("Couldn't update from template.") from e - if not skip_docker_update: + if ( + not skip_docker_update + and project + and hasattr(project, "template_metadata") + and isinstance(project.template_metadata, ProjectTemplateMetadata) + ): try: docker_updated, _, _ = update_dockerfile() except DockerfileUpdateError: diff --git a/renku/ui/service/controllers/cache_migrate_project.py b/renku/ui/service/controllers/cache_migrate_project.py index 7c2032f717..50e168301f 100644 --- a/renku/ui/service/controllers/cache_migrate_project.py +++ b/renku/ui/service/controllers/cache_migrate_project.py @@ -30,13 +30,27 @@ def execute_migration( project_path, force_template_update, skip_template_update, skip_docker_update, skip_migrations, commit_message ): """Execute project migrations.""" - from renku.command.migrate import migrate_project_command + from renku.command.migrate import ( + AUTOMATED_TEMPLATE_UPDATE_SUPPORTED, + DOCKERFILE_UPDATE_POSSIBLE, + TEMPLATE_UPDATE_POSSIBLE, + check_project, + migrate_project_command, + ) worker_log.debug(f"migrating {project_path}") communicator = ServiceCallback() with renku_project_context(project_path): + status = check_project().build().execute().output + + template_update_possible = status & TEMPLATE_UPDATE_POSSIBLE and status & AUTOMATED_TEMPLATE_UPDATE_SUPPORTED + docker_update_possible = status & DOCKERFILE_UPDATE_POSSIBLE + + skip_docker_update = skip_docker_update or not docker_update_possible + skip_template_update = skip_template_update or not template_update_possible + result = ( migrate_project_command() .with_commit(message=commit_message) From 8608c4069448943326870e020ecd9dcc3154184b Mon Sep 17 00:00:00 2001 From: Renku Bot Date: Thu, 18 Jan 2024 19:10:26 +0000 Subject: [PATCH 14/15] chore: release v2.9.1 --- CHANGES.rst | 46 +++++++++++++++++++++++++++++++ helm-chart/renku-core/Chart.yaml | 2 +- helm-chart/renku-core/values.yaml | 2 +- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 9609be926b..9c5a78fd66 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -18,6 +18,52 @@ Changes ======= +`2.9.1 `__ (2024-01-18) +------------------------------------------------------------------------------------------------------- + +Bug Fixes +~~~~~~~~~ + +- **cli:** output proper session link and only check registry if logged + in + (`#3660 `__) + (`12469f9 `__) +- **cli:** use lower case image names for sessions in upper-case + projects + (`#3666 `__) + (`ec1e282 `__) +- prevent distutils warning + (`#3663 `__) + (`5954aac `__) +- **service:** accept commit-sha in config.show + (`#3685 `__) + (`00da768 `__) +- **service:** add proper error if a dataset can’t be found + (`#3661 `__) + (`8afaedd `__) +- **service:** allow editing datasets without creator email + (`#3664 `__) + (`d74cc72 `__) +- **service:** allow setting keywords on project creation + (`#3665 `__) + (`9377ac4 `__) +- **service:** fix clone depth not being respected + (`#3678 `__) + (`0c523fa `__) +- **svc:** fix migration not working with old template metadata + (`#3687 `__) + (`398ec2e `__) + +Features +~~~~~~~~ + +- add cloud storage support for session start + (`#3629 `__) + (`ec3173a `__) +- **service:** date_published in datasets.list response + (`#3648 `__) + (`a7f4a22 `__) + `2.9.0 `__ (2024-01-17) ------------------------------------------------------------------------------------------------------- diff --git a/helm-chart/renku-core/Chart.yaml b/helm-chart/renku-core/Chart.yaml index 439f9ec1d4..5e08dc8299 100644 --- a/helm-chart/renku-core/Chart.yaml +++ b/helm-chart/renku-core/Chart.yaml @@ -3,4 +3,4 @@ appVersion: "1.0" description: A Helm chart for Kubernetes name: renku-core icon: https://avatars0.githubusercontent.com/u/53332360?s=400&u=a4311d22842343604ef61a8c8a1e5793209a67e9&v=4 -version: 2.9.0 +version: 2.9.1 diff --git a/helm-chart/renku-core/values.yaml b/helm-chart/renku-core/values.yaml index 70cc73c055..9c8bc7658d 100644 --- a/helm-chart/renku-core/values.yaml +++ b/helm-chart/renku-core/values.yaml @@ -8,4 +8,4 @@ global: versions: latest: image: - tag: v2.9.0 + tag: v2.9.1 From fafec5ac7f8ca5356cba6e8ac37065e49d39be46 Mon Sep 17 00:00:00 2001 From: Ralf Grubenmann Date: Thu, 18 Jan 2024 20:33:42 +0100 Subject: [PATCH 15/15] update changelog --- CHANGES.rst | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 9c5a78fd66..ca3bb6165f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -24,45 +24,10 @@ Changes Bug Fixes ~~~~~~~~~ -- **cli:** output proper session link and only check registry if logged - in - (`#3660 `__) - (`12469f9 `__) -- **cli:** use lower case image names for sessions in upper-case - projects - (`#3666 `__) - (`ec1e282 `__) -- prevent distutils warning - (`#3663 `__) - (`5954aac `__) -- **service:** accept commit-sha in config.show - (`#3685 `__) - (`00da768 `__) -- **service:** add proper error if a dataset can’t be found - (`#3661 `__) - (`8afaedd `__) -- **service:** allow editing datasets without creator email - (`#3664 `__) - (`d74cc72 `__) -- **service:** allow setting keywords on project creation - (`#3665 `__) - (`9377ac4 `__) -- **service:** fix clone depth not being respected - (`#3678 `__) - (`0c523fa `__) - **svc:** fix migration not working with old template metadata (`#3687 `__) (`398ec2e `__) -Features -~~~~~~~~ - -- add cloud storage support for session start - (`#3629 `__) - (`ec3173a `__) -- **service:** date_published in datasets.list response - (`#3648 `__) - (`a7f4a22 `__) `2.9.0 `__ (2024-01-17) -------------------------------------------------------------------------------------------------------