From 08e8f28328504164aa4ce6bf90a5254aa03bcacc Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Thu, 14 Nov 2019 14:13:34 +0100 Subject: [PATCH 001/264] Remove support of python version older than 3.6 --- README.rst | 7 ++ integration/config_test.py | 3 +- integration/imported.py | 15 ----- integration/test_applications.py | 15 +++-- main/cloudfoundry_client/client.py | 6 +- main/cloudfoundry_client/doppler/client.py | 7 +- main/cloudfoundry_client/errors.py | 9 +-- main/cloudfoundry_client/imported.py | 29 -------- .../main/command_domain.py | 6 +- main/cloudfoundry_client/main/main.py | 4 +- main/cloudfoundry_client/v2/apps.py | 4 +- main/cloudfoundry_client/v2/entities.py | 8 +-- main/cloudfoundry_client/v3/entities.py | 6 +- setup.py | 4 -- test/abstract_test_case.py | 5 +- test/fake_requests.py | 15 +++-- test/imported.py | 23 ------- test/operations/push/test_cf_ignore.py | 22 +++--- test/operations/push/test_push.py | 3 +- test/requirements.txt | 2 - test/test_client.py | 21 +++--- test/v2/test_apps.py | 63 ++++++++--------- test/v2/test_buildpacks.py | 10 +-- test/v2/test_doppler.py | 5 +- test/v2/test_entities.py | 13 ++-- test/v2/test_events.py | 4 +- test/v2/test_organizations.py | 17 ++--- test/v2/test_routes.py | 21 +++--- test/v2/test_service_bindings.py | 23 ++++--- test/v2/test_service_brokers.py | 19 +++--- test/v2/test_service_instances.py | 67 ++++++++++--------- test/v2/test_service_keys.py | 21 +++--- test/v2/test_service_plan_visibilities.py | 23 ++++--- test/v2/test_service_plans.py | 21 +++--- test/v2/test_services.py | 17 ++--- test/v2/test_spaces.py | 21 +++--- test/v3/test_apps.py | 19 +++--- test/v3/test_buildpacks.py | 17 +++-- test/v3/test_organizations.py | 6 +- test/v3/test_service_instances.py | 6 +- test/v3/test_spaces.py | 12 ++-- test/v3/test_tasks.py | 18 ++--- 42 files changed, 295 insertions(+), 342 deletions(-) delete mode 100644 integration/imported.py delete mode 100644 main/cloudfoundry_client/imported.py delete mode 100644 test/imported.py delete mode 100644 test/requirements.txt diff --git a/README.rst b/README.rst index 81d4cdd..0c9d71b 100644 --- a/README.rst +++ b/README.rst @@ -11,6 +11,13 @@ The cf-python-client repo contains a Python client library for Cloud Foundry. Installing ---------- +Supported versions +~~~~~~~~~~~~~~~~~~ + +:warning: Starting version `1.11.0`, versions older that python `3.6.0` will not be supported anymore. This late version was released by the end 2016. + +For those that are still using python 2.7, it won't be supported by the end of 2020 and all library shall stop supporting it. + From pip ~~~~~~~~ diff --git a/integration/config_test.py b/integration/config_test.py index 6022e65..b80cc9f 100644 --- a/integration/config_test.py +++ b/integration/config_test.py @@ -1,7 +1,8 @@ import logging import os +from configparser import ConfigParser, NoSectionError, NoOptionError +from http.client import HTTPConnection -from imported import ConfigParser, NoSectionError, NoOptionError, HTTPConnection from cloudfoundry_client.client import CloudFoundryClient _client = None diff --git a/integration/imported.py b/integration/imported.py deleted file mode 100644 index 3734e77..0000000 --- a/integration/imported.py +++ /dev/null @@ -1,15 +0,0 @@ -import sys - -if sys.version_info.major == 2: - from ConfigParser import ConfigParser, NoSectionError, NoOptionError - from httplib import SEE_OTHER, CREATED, NO_CONTENT,HTTPConnection - -elif sys.version_info.major == 3: - from configparser import ConfigParser, NoSectionError, NoOptionError - from http import HTTPStatus - SEE_OTHER = HTTPStatus.SEE_OTHER.value - CREATED = HTTPStatus.CREATED.value - NO_CONTENT = HTTPStatus.NO_CONTENT.value - from http.client import HTTPConnection -else: - raise ImportError('Invalid major version: %d' % sys.version_info.major) \ No newline at end of file diff --git a/integration/test_applications.py b/integration/test_applications.py index 203f870..a65be31 100644 --- a/integration/test_applications.py +++ b/integration/test_applications.py @@ -1,10 +1,11 @@ -from config_test import build_client_from_configuration -import unittest -import logging import json +import logging +import unittest +from http import HTTPStatus + +from config_test import build_client_from_configuration from cloudfoundry_client.errors import InvalidStatusCode -from cloudfoundry_client.imported import NOT_FOUND, BAD_REQUEST _logger = logging.getLogger(__name__) @@ -24,7 +25,7 @@ def test_list(self): client.v2.apps.get('%s-0' % application['metadata']['guid']) self.fail('Should not have been found') except InvalidStatusCode as e: - self.assertEquals(e.status_code, NOT_FOUND) + self.assertEquals(e.status_code, HTTPStatus.NOT_FOUND) try: instances = client.v2.apps.get_instances(application['metadata']['guid']) self.assertIsNotNone(instances) @@ -32,7 +33,7 @@ def test_list(self): _logger.debug('instances = %s', json.dumps(instances)) except InvalidStatusCode as e: #instance is stopped - self.assertEquals(e.status_code, BAD_REQUEST) + self.assertEquals(e.status_code, HTTPStatus.BAD_REQUEST) self.assertIsInstance(e.body, dict) self.assertEqual(e.body.get('error_code'), 'CF-InstancesError') try: @@ -43,7 +44,7 @@ def test_list(self): _logger.debug('stats = %s', json.dumps(stats)) except InvalidStatusCode as e: # instance is stopped - self.assertEquals(e.status_code, BAD_REQUEST) + self.assertEquals(e.status_code, HTTPStatus.BAD_REQUEST) self.assertIsInstance(e.body, dict) self.assertEqual(e.body.get('error_code'), 'CF-AppStoppedStatsError') env = client.v2.apps.get_env(application['metadata']['guid']) diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 7b65784..8eeab97 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -1,15 +1,15 @@ import logging +from http import HTTPStatus import requests from oauth2_client.credentials_manager import CredentialManager, ServiceInformation from cloudfoundry_client.doppler.client import DopplerClient from cloudfoundry_client.errors import InvalidStatusCode -from cloudfoundry_client.imported import UNAUTHORIZED from cloudfoundry_client.v2.apps import AppManager as AppManagerV2 from cloudfoundry_client.v2.buildpacks import BuildpackManager as BuildpackManagerV2 -from cloudfoundry_client.v2.events import EventManager from cloudfoundry_client.v2.entities import EntityManager as EntityManagerV2 +from cloudfoundry_client.v2.events import EventManager from cloudfoundry_client.v2.jobs import JobManager from cloudfoundry_client.v2.resources import ResourceManager from cloudfoundry_client.v2.routes import RouteManager @@ -133,7 +133,7 @@ def _get_info(target_endpoint, proxy=None, verify=True): @staticmethod def _is_token_expired(response): - if response.status_code == UNAUTHORIZED: + if response.status_code == HTTPStatus.UNAUTHORIZED.value: try: json_data = response.json() result = json_data.get('code', 0) == 1000 and json_data.get('error_code', '') == 'CF-InvalidAuthToken' diff --git a/main/cloudfoundry_client/doppler/client.py b/main/cloudfoundry_client/doppler/client.py index 622985c..c2074a8 100644 --- a/main/cloudfoundry_client/doppler/client.py +++ b/main/cloudfoundry_client/doppler/client.py @@ -1,8 +1,7 @@ import logging import re -from cloudfoundry_client.imported import urlparse +from urllib.parse import urlparse -from cloudfoundry_client.imported import bufferize_string from cloudfoundry_client.doppler.websocket_envelope_reader import WebsocketFrameReader from cloudfoundry_client.dropsonde.envelope_pb2 import Envelope from cloudfoundry_client.errors import InvalidLogResponseException @@ -66,8 +65,8 @@ def _extract_boundary(response): @staticmethod def _read_multi_part_response(iterable, boundary): remaining = '' - boundary_header = bufferize_string('--%s' % boundary) - end_of_line = bufferize_string('\r\n') + boundary_header = bytes('--%s' % boundary, 'UTF-8') + end_of_line = bytes('\r\n', 'UTF-8') cpt_read = 0 for chunk_data in iterable: # _logger.debug('reading %d bytes' % size) diff --git a/main/cloudfoundry_client/errors.py b/main/cloudfoundry_client/errors.py index 5d9f498..428d504 100644 --- a/main/cloudfoundry_client/errors.py +++ b/main/cloudfoundry_client/errors.py @@ -1,4 +1,5 @@ import json +from http import HTTPStatus class InvalidLogResponseException(Exception): @@ -6,17 +7,17 @@ class InvalidLogResponseException(Exception): class InvalidStatusCode(Exception): - def __init__(self, status_code, body): + def __init__(self, status_code: HTTPStatus, body): self.status_code = status_code self.body = body def __str__(self): if self.body is None: - return '%d' % self.status_code + return '%d' % self.status_code.value elif type(self.body) == str: - return '%d : %s' % (self.status_code, self.body) + return '%d : %s' % (self.status_code.value, self.body) else: - return '%d : %s' % (self.status_code, json.dumps(self.body)) + return '%d : %s' % (self.status_code.value, json.dumps(self.body)) class InvalidEntity(Exception): diff --git a/main/cloudfoundry_client/imported.py b/main/cloudfoundry_client/imported.py deleted file mode 100644 index 9cb4b4b..0000000 --- a/main/cloudfoundry_client/imported.py +++ /dev/null @@ -1,29 +0,0 @@ -import sys - -import requests - -if sys.version_info.major == 2: - - from httplib import UNAUTHORIZED, BAD_REQUEST, NOT_FOUND, OK - from urllib import quote - from urlparse import urlparse - - requests.packages.urllib3.disable_warnings() - from __builtin__ import reduce - - def bufferize_string(content): - return content -elif sys.version_info.major == 3: - from http import HTTPStatus - UNAUTHORIZED = HTTPStatus.UNAUTHORIZED.value - BAD_REQUEST = HTTPStatus.BAD_REQUEST.value - NOT_FOUND = HTTPStatus.NOT_FOUND.value - OK = HTTPStatus.OK.value - from urllib.parse import quote, urlparse - from functools import reduce - - def bufferize_string(content): - return bytes(content, 'UTF-8') - -else: - raise ImportError('Invalid major version: %d' % sys.version_info.major) diff --git a/main/cloudfoundry_client/main/command_domain.py b/main/cloudfoundry_client/main/command_domain.py index c74c27f..4587e54 100644 --- a/main/cloudfoundry_client/main/command_domain.py +++ b/main/cloudfoundry_client/main/command_domain.py @@ -3,9 +3,9 @@ import os import re from collections import OrderedDict +from http import HTTPStatus from cloudfoundry_client.errors import InvalidStatusCode -from cloudfoundry_client.imported import NOT_FOUND class Command(object): @@ -99,7 +99,7 @@ def resolve_id(self, argument, get_by_name): elif self.api_version == 'v3': return result['guid'] else: - raise InvalidStatusCode(NOT_FOUND, '%s with name %s' % (self.client_domain, argument)) + raise InvalidStatusCode(HTTPStatus.NOT_FOUND, '%s with name %s' % (self.client_domain, argument)) else: raise ValueError('id: %s: does not allow search by name' % self.client_domain) @@ -147,7 +147,7 @@ def execute(client, arguments): elif self.allow_retrieve_by_name: entity = self.find_by_name(client, arguments.id[0]) if entity is None: - raise InvalidStatusCode(NOT_FOUND, '%s with name %s' % (self.client_domain, arguments.id[0])) + raise InvalidStatusCode(HTTPStatus.NOT_FOUND, '%s with name %s' % (self.client_domain, arguments.id[0])) else: self._get_client_domain(client)._remove(self.id(entity)) else: diff --git a/main/cloudfoundry_client/main/main.py b/main/cloudfoundry_client/main/main.py index b9cb3f7..434790b 100644 --- a/main/cloudfoundry_client/main/main.py +++ b/main/cloudfoundry_client/main/main.py @@ -5,13 +5,13 @@ import os import re import sys +from http import HTTPStatus from requests.exceptions import ConnectionError from cloudfoundry_client import __version__ from cloudfoundry_client.client import CloudFoundryClient from cloudfoundry_client.errors import InvalidStatusCode -from cloudfoundry_client.imported import NOT_FOUND from cloudfoundry_client.main.apps_command_domain import AppCommandDomain from cloudfoundry_client.main.command_domain import CommandDomain, Command from cloudfoundry_client.main.operation_commands import generate_push_command @@ -122,7 +122,7 @@ def resolve_id(argument, get_by_name, domain_name, allow_search_by_name): if result is not None: return result['metadata']['guid'] else: - raise InvalidStatusCode(NOT_FOUND, '%s with name %s' % (domain_name, argument)) + raise InvalidStatusCode(HTTPStatus.NOT_FOUND, '%s with name %s' % (domain_name, argument)) else: raise ValueError('id: %s: does not allow search by name' % domain_name) diff --git a/main/cloudfoundry_client/v2/apps.py b/main/cloudfoundry_client/v2/apps.py index 9fea414..6564229 100644 --- a/main/cloudfoundry_client/v2/apps.py +++ b/main/cloudfoundry_client/v2/apps.py @@ -1,10 +1,10 @@ import json import logging import os +from http import HTTPStatus from time import sleep from cloudfoundry_client.errors import InvalidStatusCode -from cloudfoundry_client.imported import BAD_REQUEST from cloudfoundry_client.json_object import JsonObject from cloudfoundry_client.v2.entities import Entity, EntityManager @@ -150,7 +150,7 @@ def _safe_get_instances(self, application_guid): try: return self.get_instances(application_guid) except InvalidStatusCode as ex: - if ex.status_code == BAD_REQUEST and type(ex.body) == dict: + if ex.status_code == HTTPStatus.BAD_REQUEST and type(ex.body) == dict: code = ex.body.get('code', -1) # 170002: staging not finished # 220001: instances error diff --git a/main/cloudfoundry_client/v2/entities.py b/main/cloudfoundry_client/v2/entities.py index 777411e..b186b73 100644 --- a/main/cloudfoundry_client/v2/entities.py +++ b/main/cloudfoundry_client/v2/entities.py @@ -1,8 +1,8 @@ -import functools import logging +from functools import partial, reduce +from urllib.parse import quote from cloudfoundry_client.errors import InvalidEntity -from cloudfoundry_client.imported import quote, reduce from cloudfoundry_client.json_object import JsonObject from cloudfoundry_client.request_object import Request @@ -32,9 +32,9 @@ def __init__(self, target_endpoint, client, *args, **kwargs): client, '') if domain_name.endswith('s'): - new_method = functools.partial(other_manager._list, value) + new_method = partial(other_manager._list, value) else: - new_method = functools.partial(other_manager._get, value) + new_method = partial(other_manager._get, value) new_method.__name__ = domain_name setattr(self, domain_name, new_method) except KeyError: diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index 7b38cd1..868717c 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -1,8 +1,8 @@ -import logging import functools +import logging +from urllib.parse import quote from cloudfoundry_client.errors import InvalidEntity -from cloudfoundry_client.imported import quote, reduce from cloudfoundry_client.json_object import JsonObject from cloudfoundry_client.request_object import Request @@ -152,6 +152,6 @@ def _append_encoded_parameter(parameters, args): if len(kwargs) > 0: return '%s?%s' % (url, - "&".join(reduce(_append_encoded_parameter, sorted(list(kwargs.items())), []))) + "&".join(functools.reduce(_append_encoded_parameter, sorted(list(kwargs.items())), []))) else: return url diff --git a/setup.py b/setup.py index 5916293..1ed52dc 100644 --- a/setup.py +++ b/setup.py @@ -62,11 +62,7 @@ def run(self): "Programming Language :: Python", "Natural Language :: English", "Operating System :: OS Independent", - "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.3", - "Programming Language :: Python :: 3.4", - "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Topic :: Communications", diff --git a/test/abstract_test_case.py b/test/abstract_test_case.py index b5ac2a4..92d5b88 100644 --- a/test/abstract_test_case.py +++ b/test/abstract_test_case.py @@ -1,10 +1,11 @@ import json +from http import HTTPStatus +from unittest.mock import MagicMock, patch from oauth2_client.credentials_manager import CredentialManager from cloudfoundry_client.client import CloudFoundryClient from fake_requests import TARGET_ENDPOINT, mock_response -from imported import MagicMock, patch def mock_cloudfoundry_client_class(): @@ -28,7 +29,7 @@ def mock_client_class(cls): def build_client(self): with patch('cloudfoundry_client.client.requests') as fake_requests: - fake_info_response = mock_response('/v2/info', 200, None) + fake_info_response = mock_response('/v2/info', HTTPStatus.OK, None) fake_info_response.text = json.dumps(dict(api_version='2.X', authorization_endpoint=TARGET_ENDPOINT)) fake_requests.get.return_value = fake_info_response diff --git a/test/fake_requests.py b/test/fake_requests.py index 5b477c9..9cddcc2 100644 --- a/test/fake_requests.py +++ b/test/fake_requests.py @@ -1,7 +1,12 @@ import os +from http import HTTPStatus from json import loads +from unittest.mock import MagicMock -from imported import SEE_OTHER, iterate_text, MagicMock + +def iterate_text(text): + for character in text: + yield bytes([character]) class MockSession(object): @@ -13,12 +18,12 @@ def __init__(self): class MockResponse(object): - def __init__(self, url, status_code, text, headers=None): - self.status_code = status_code + def __init__(self, url: str, status_code: HTTPStatus, text: str, headers: dict = None): + self.status_code = status_code.value self.url = url self.text = text self.headers = dict() - self.is_redirect = status_code == SEE_OTHER + self.is_redirect = status_code == HTTPStatus.SEE_OTHER if headers is not None: self.headers.update(headers) @@ -39,7 +44,7 @@ def get_fixtures_path(*paths): return os.path.join(os.path.dirname(__file__), 'fixtures', *paths) -def mock_response(uri, status_code, headers, *path_parts): +def mock_response(uri: str, status_code: HTTPStatus, headers: dict, *path_parts: str): global TARGET_ENDPOINT if len(path_parts) > 0: file_name = path_parts[len(path_parts) - 1] diff --git a/test/imported.py b/test/imported.py deleted file mode 100644 index 643e014..0000000 --- a/test/imported.py +++ /dev/null @@ -1,23 +0,0 @@ -import sys - -if sys.version_info.major == 2: - from mock import patch, call, MagicMock, mock_open - from httplib import SEE_OTHER, CREATED, NO_CONTENT, ACCEPTED - def iterate_text(text): - for character in text: - yield character - built_in_entry = '__builtin__' - -elif sys.version_info.major == 3: - from unittest.mock import patch, call, MagicMock, mock_open - from http import HTTPStatus - SEE_OTHER = HTTPStatus.SEE_OTHER.value - CREATED = HTTPStatus.CREATED.value - NO_CONTENT = HTTPStatus.NO_CONTENT.value - ACCEPTED = HTTPStatus.ACCEPTED.value - def iterate_text(text): - for character in text: - yield bytes([character]) - built_in_entry = 'builtins' -else: - raise ImportError('Invalid major version: %d' % sys.version_info.major) \ No newline at end of file diff --git a/test/operations/push/test_cf_ignore.py b/test/operations/push/test_cf_ignore.py index 6eb4f14..ae4124a 100644 --- a/test/operations/push/test_cf_ignore.py +++ b/test/operations/push/test_cf_ignore.py @@ -1,14 +1,14 @@ import os from unittest import TestCase +from unittest.mock import mock_open, patch from cloudfoundry_client.operations.push.cf_ignore import CfIgnore -from imported import patch, mock_open, built_in_entry class TestCfIgnore(TestCase): def test_open_cfignore_file(self): - with patch("%s.open" % built_in_entry, mock_open(read_data="*.log")) as mock_file, \ + with patch("builtins.open", mock_open(read_data="*.log")) as mock_file, \ patch('os.path.isfile', create=True) as mock_isfile: mock_isfile.__return_value__ = True application_path = '/some/path' @@ -17,8 +17,8 @@ def test_open_cfignore_file(self): mock_file.assert_called_with(os.path.join(application_path, '.cfignore'), 'r') def test_ignore_wildcard_resources(self): - with patch("%s.open" % built_in_entry, mock_open(read_data="*.log")), \ - patch('os.path.isfile', create=True) as mock_isfile: + with patch("builtins.open", mock_open(read_data="*.log")), \ + patch('os.path.isfile', create=True) as mock_isfile: mock_isfile.__return_value__ = True cf_ignore = CfIgnore('/some/path') @@ -26,27 +26,27 @@ def test_ignore_wildcard_resources(self): self.assertTrue(cf_ignore.is_entry_ignored("/some/other/path/toto.log")) def test_ignore_directory(self): - with patch("%s.open" % built_in_entry, mock_open(read_data="ignored/directory/")), \ - patch('os.path.isfile', create=True) as mock_isfile: + with patch("builtins.open", mock_open(read_data="ignored/directory/")), \ + patch('os.path.isfile', create=True) as mock_isfile: mock_isfile.__return_value__ = True cf_ignore = CfIgnore('/some/path') self.assertTrue(cf_ignore.is_entry_ignored("ignored/directory/resource.file")) self.assertTrue(cf_ignore.is_entry_ignored("/ignored/directory/resource.file")) - self.assertTrue(cf_ignore.is_entry_ignored("/some/sub/directory/containing/ignored/directory/resource.file")) + self.assertTrue( + cf_ignore.is_entry_ignored("/some/sub/directory/containing/ignored/directory/resource.file")) # File in fact self.assertFalse(cf_ignore.is_entry_ignored('/ignored/directory')) def test_ignore_file_with_directory(self): - with patch("%s.open" % built_in_entry, mock_open(read_data="ignored/directory/resource.file")), \ + with patch("builtins.open", mock_open(read_data="ignored/directory/resource.file")), \ patch('os.path.isfile', create=True) as mock_isfile: mock_isfile.__return_value__ = True cf_ignore = CfIgnore('/some/path') self.assertTrue(cf_ignore.is_entry_ignored("ignored/directory/resource.file")) self.assertTrue(cf_ignore.is_entry_ignored("/ignored/directory/resource.file")) - self.assertTrue(cf_ignore.is_entry_ignored("/some/sub/directory/containing/ignored/directory/resource.file")) + self.assertTrue( + cf_ignore.is_entry_ignored("/some/sub/directory/containing/ignored/directory/resource.file")) # File in fact self.assertFalse(cf_ignore.is_entry_ignored('ignored/resource.file')) - - diff --git a/test/operations/push/test_push.py b/test/operations/push/test_push.py index c4ec3ab..f4eeca3 100644 --- a/test/operations/push/test_push.py +++ b/test/operations/push/test_push.py @@ -1,11 +1,11 @@ import sys from unittest import TestCase +from unittest.mock import patch, MagicMock import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase from cloudfoundry_client.operations.push.push import PushOperation from fake_requests import get_fixtures_path -from imported import patch, MagicMock class TestPushOperation(TestCase, AbstractTestCase): @@ -50,6 +50,7 @@ def test_main_push(self): class FakeOperation(object): def __init__(self): self.push = MagicMock() + client = object() push_operation = FakeOperation() with patch('cloudfoundry_client.main.main.build_client_from_configuration', diff --git a/test/requirements.txt b/test/requirements.txt deleted file mode 100644 index 59b3290..0000000 --- a/test/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -# only on python 2 -mock==2.0.0 diff --git a/test/test_client.py b/test/test_client.py index 528f487..5c1af8e 100644 --- a/test/test_client.py +++ b/test/test_client.py @@ -1,21 +1,20 @@ import json import unittest +from http import HTTPStatus +from unittest.mock import patch +from urllib.parse import quote from abstract_test_case import AbstractTestCase from cloudfoundry_client.client import CloudFoundryClient -from cloudfoundry_client.imported import OK -from cloudfoundry_client.imported import quote from fake_requests import MockResponse, MockSession, FakeRequests from fake_requests import TARGET_ENDPOINT -from imported import patch -class TestCloudfoundryClient(unittest.TestCase, AbstractTestCase,): +class TestCloudfoundryClient(unittest.TestCase, AbstractTestCase, ): @classmethod def setUpClass(cls): cls.mock_client_class() - def test_grant_password_request_with_token_format_opaque(self): requests = FakeRequests() session = MockSession() @@ -23,11 +22,11 @@ def test_grant_password_request_with_token_format_opaque(self): patch('cloudfoundry_client.client.requests', new=requests): requests.Session.return_value = session requests.get.return_value = MockResponse('%s/v2/info' % TARGET_ENDPOINT, - status_code=OK, + status_code=HTTPStatus.OK, text=json.dumps(dict(api_version='2.1', authorization_endpoint=TARGET_ENDPOINT))) requests.post.return_value = MockResponse('%s/oauth/token' % TARGET_ENDPOINT, - status_code=OK, + status_code=HTTPStatus.OK, text=json.dumps(dict(access_token='access-token', refresh_token='refresh-token'))) client = CloudFoundryClient(TARGET_ENDPOINT, token_format='opaque') @@ -50,11 +49,11 @@ def test_refresh_request_with_token_format_opaque(self): patch('cloudfoundry_client.client.requests', new=requests): requests.Session.return_value = session requests.get.return_value = MockResponse('%s/v2/info' % TARGET_ENDPOINT, - status_code=OK, + status_code=HTTPStatus.OK, text=json.dumps(dict(api_version='2.1', authorization_endpoint=TARGET_ENDPOINT))) requests.post.return_value = MockResponse('%s/oauth/token' % TARGET_ENDPOINT, - status_code=OK, + status_code=HTTPStatus.OK, text=json.dumps(dict(access_token='access-token', refresh_token='refresh-token'))) client = CloudFoundryClient(TARGET_ENDPOINT, token_format='opaque') @@ -76,11 +75,11 @@ def test_grant_password_request_with_login_hint(self): patch('cloudfoundry_client.client.requests', new=requests): requests.Session.return_value = session requests.get.return_value = MockResponse('%s/v2/info' % TARGET_ENDPOINT, - status_code=OK, + status_code=HTTPStatus.OK, text=json.dumps(dict(api_version='2.1', authorization_endpoint=TARGET_ENDPOINT))) requests.post.return_value = MockResponse('%s/oauth/token' % TARGET_ENDPOINT, - status_code=OK, + status_code=HTTPStatus.OK, text=json.dumps(dict(access_token='access-token', refresh_token='refresh-token'))) client = CloudFoundryClient(TARGET_ENDPOINT, login_hint=quote(json.dumps(dict(origin='uaa'), diff --git a/test/v2/test_apps.py b/test/v2/test_apps.py index 4391c20..9a30a99 100644 --- a/test/v2/test_apps.py +++ b/test/v2/test_apps.py @@ -1,12 +1,13 @@ import sys import unittest +from functools import reduce +from http import HTTPStatus +from unittest.mock import call, patch import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase from cloudfoundry_client.errors import InvalidStatusCode -from cloudfoundry_client.imported import BAD_REQUEST, OK, reduce from fake_requests import mock_response, TARGET_ENDPOINT -from imported import CREATED, patch, call, NO_CONTENT class TestApps(unittest.TestCase, AbstractTestCase): @@ -19,7 +20,7 @@ def setUp(self): def test_list(self): self.client.get.return_value = mock_response('/v2/apps', - OK, + HTTPStatus.OK, None, 'v2', 'apps', 'GET_response.json') all_applications = [application for application in self.client.v2.apps.list()] @@ -31,7 +32,7 @@ def test_list(self): def test_list_filtered(self): self.client.get.return_value = mock_response( '/v2/apps?q=name%3Aapplication_name&results-per-page=1&q=space_guid%3Aspace_guid', - OK, + HTTPStatus.OK, None, 'v2', 'apps', 'GET_space_guid_name_response.json') application = self.client.v2.apps.get_first(space_guid='space_guid', name='application_name') @@ -41,7 +42,7 @@ def test_list_filtered(self): def test_get_env(self): self.client.get.return_value = mock_response( '/v2/apps/app_id/env', - OK, + HTTPStatus.OK, None, 'v2', 'apps', 'GET_{id}_env_response.json') application = self.client.v2.apps.get_env('app_id') @@ -51,7 +52,7 @@ def test_get_env(self): def test_get_instances(self): self.client.get.return_value = mock_response( '/v2/apps/app_id/instances', - OK, + HTTPStatus.OK, None, 'v2', 'apps', 'GET_{id}_instances_response.json') application = self.client.v2.apps.get_instances('app_id') @@ -61,7 +62,7 @@ def test_get_instances(self): def test_get_stats(self): self.client.get.return_value = mock_response( '/v2/apps/app_id/stats', - OK, + HTTPStatus.OK, None, 'v2', 'apps', 'GET_{id}_stats_response.json') application = self.client.v2.apps.get_stats('app_id') @@ -69,7 +70,7 @@ def test_get_stats(self): self.assertIsNotNone(application) def test_associate_route(self): - self.client.put.return_value = mock_response('/v2/apps/app_id/routes/route_id', CREATED, None, + self.client.put.return_value = mock_response('/v2/apps/app_id/routes/route_id', HTTPStatus.CREATED, None, 'v2', 'apps', 'PUT_{id}_routes_{route_id}_response.json') self.client.v2.apps.associate_route('app_id', 'route_id') self.client.put.assert_called_with(self.client.put.return_value.url, json=None) @@ -77,7 +78,7 @@ def test_associate_route(self): def test_list_routes(self): self.client.get.return_value = mock_response( '/v2/apps/app_id/routes?q=route_guid%3Aroute_id', - OK, + HTTPStatus.OK, None, 'v2', 'apps', 'GET_{id}_routes_response.json') cpt = reduce(lambda increment, _: increment + 1, @@ -88,14 +89,14 @@ def test_list_routes(self): self.assertEqual(cpt, 1) def test_remove_route(self): - self.client.delete.return_value = mock_response('/v2/apps/app_id/routes/route_id', NO_CONTENT, None) + self.client.delete.return_value = mock_response('/v2/apps/app_id/routes/route_id', HTTPStatus.NO_CONTENT, None) self.client.v2.apps.remove_route('app_id', 'route_id') self.client.delete.assert_called_with(self.client.delete.return_value.url) def test_list_service_bindings(self): self.client.get.return_value = mock_response( '/v2/apps/app_id/service_bindings', - OK, + HTTPStatus.OK, None, 'v2', 'apps', 'GET_{id}_service_bindings_response.json') cpt = reduce(lambda increment, _: increment + 1, @@ -106,7 +107,7 @@ def test_list_service_bindings(self): def test_get_sumary(self): self.client.get.return_value = mock_response( '/v2/apps/app_id/summary', - OK, + HTTPStatus.OK, None, 'v2', 'apps', 'GET_{id}_summary_response.json') application = self.client.v2.apps.get_summary('app_id') @@ -117,7 +118,7 @@ def test_get_sumary(self): def test_get(self): self.client.get.return_value = mock_response( '/v2/apps/app_id', - OK, + HTTPStatus.OK, None, 'v2', 'apps', 'GET_{id}_response.json') application = self.client.v2.apps.get('app_id') @@ -127,26 +128,26 @@ def test_get(self): def test_start(self): self.client.put.return_value = mock_response( '/v2/apps/app_id', - CREATED, + HTTPStatus.CREATED, None, 'v2', 'apps', 'PUT_{id}_response.json') mock_summary = mock_response( '/v2/apps/app_id/summary', - OK, + HTTPStatus.OK, None, 'v2', 'apps', 'GET_{id}_summary_response.json') mock_instances_stopped = mock_response( '/v2/apps/app_id/instances', - BAD_REQUEST, + HTTPStatus.BAD_REQUEST, None, 'v2', 'apps', 'GET_{id}_instances_stopped_response.json') mock_instances_started = mock_response( '/v2/apps/app_id/instances', - OK, + HTTPStatus.OK, None, 'v2', 'apps', 'GET_{id}_instances_response.json') self.client.get.side_effect = [mock_summary, - InvalidStatusCode(BAD_REQUEST, dict(code=220001)), + InvalidStatusCode(HTTPStatus.BAD_REQUEST, dict(code=220001)), mock_instances_started] application = self.client.v2.apps.start('app_id') @@ -161,10 +162,10 @@ def test_start(self): def test_stop(self): self.client.put.return_value = mock_response( '/v2/apps/app_id', - CREATED, + HTTPStatus.CREATED, None, 'v2', 'apps', 'PUT_{id}_response.json') - self.client.get.side_effect = [InvalidStatusCode(BAD_REQUEST, dict(code=220001))] + self.client.get.side_effect = [InvalidStatusCode(HTTPStatus.BAD_REQUEST, dict(code=220001))] application = self.client.v2.apps.stop('app_id') self.client.put.assert_called_with(self.client.put.return_value.url, json=dict(state='STOPPED')) @@ -174,7 +175,7 @@ def test_stop(self): def test_create(self): self.client.post.return_value = mock_response( '/v2/apps', - CREATED, + HTTPStatus.CREATED, None, 'v2', 'apps', 'POST_response.json') application = self.client.v2.apps.create(name='test', space_guid='1fbb3e81-4f55-4fd3-9820-45febbd5e53e', @@ -190,7 +191,7 @@ def test_create(self): def test_update(self): self.client.put.return_value = mock_response( '/v2/apps/app_id', - CREATED, + HTTPStatus.CREATED, None, 'v2', 'apps', 'PUT_{id}_response.json') application = self.client.v2.apps.update('app_id', stack_guid='82f9c01c-72f2-4d3e-b5ed-eab97a6203cf', @@ -204,14 +205,14 @@ def test_update(self): def test_remove(self): self.client.delete.return_value = mock_response( '/v2/apps/app_id', - NO_CONTENT, None) + HTTPStatus.NO_CONTENT, None) self.client.v2.apps.remove('app_id') self.client.delete.assert_called_with(self.client.delete.return_value.url) def test_restage(self): self.client.post.return_value = mock_response( '/v2/apps/app_id/restage', - CREATED, + HTTPStatus.CREATED, None, 'v2', 'apps', 'POST_{id}_restage_response.json') self.client.v2.apps.restage('app_id') @@ -221,18 +222,18 @@ def test_entity(self): self.client.get.side_effect = [ mock_response( '/v2/apps/app_id', - OK, + HTTPStatus.OK, None, 'v2', 'apps', 'GET_{id}_response.json'), mock_response( '/v2/spaces/space_id', - OK, + HTTPStatus.OK, None, 'v2', 'spaces', 'GET_{id}_response.json') , mock_response( '/v2/routes', - OK, + HTTPStatus.OK, None, 'v2', 'routes', 'GET_response.json') ] @@ -249,7 +250,7 @@ def test_main_list_apps(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.get.return_value = mock_response('/v2/apps', - OK, + HTTPStatus.OK, None, 'v2', 'apps', 'GET_response.json') main.main() @@ -260,7 +261,7 @@ def test_main_delete_apps(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.delete.return_value = mock_response('/v2/apps/906775ea-622e-4bc7-af5d-9aab3b652f81', - NO_CONTENT, + HTTPStatus.NO_CONTENT, None) main.main() self.client.delete.assert_called_with(self.client.delete.return_value.url) @@ -270,7 +271,7 @@ def test_main_get_app(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.get.return_value = mock_response('/v2/apps/906775ea-622e-4bc7-af5d-9aab3b652f81', - OK, + HTTPStatus.OK, None, 'v2', 'apps', 'GET_{id}_response.json') main.main() @@ -281,7 +282,7 @@ def test_main_restage_app(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.post.return_value = mock_response('/v2/apps/906775ea-622e-4bc7-af5d-9aab3b652f81/restage', - CREATED, + HTTPStatus.CREATED, None, 'v2', 'apps', 'POST_{id}_restage_response.json') main.main() diff --git a/test/v2/test_buildpacks.py b/test/v2/test_buildpacks.py index c63d6fb..ce01678 100644 --- a/test/v2/test_buildpacks.py +++ b/test/v2/test_buildpacks.py @@ -1,9 +1,9 @@ import unittest +from functools import reduce +from http import HTTPStatus from abstract_test_case import AbstractTestCase -from cloudfoundry_client.imported import OK, reduce from fake_requests import mock_response -from imported import CREATED, patch class TestBuildpacks(unittest.TestCase, AbstractTestCase): @@ -16,7 +16,7 @@ def setUp(self): def test_list(self): self.client.get.return_value = mock_response('/v2/buildpacks', - OK, + HTTPStatus.OK, None, 'v2', 'buildpacks', 'GET_response.json') cpt = reduce(lambda increment, _: increment + 1, self.client.v2.buildpacks.list(), 0) @@ -26,7 +26,7 @@ def test_list(self): def test_get(self): self.client.get.return_value = mock_response( '/v2/buildpacks/buildpack_id', - OK, + HTTPStatus.OK, None, 'v2', 'buildpacks', 'GET_{id}_response.json') result = self.client.v2.buildpacks.get('buildpack_id') @@ -36,7 +36,7 @@ def test_get(self): def test_update(self): self.client.put.return_value = mock_response( '/v2/buildpacks/build_pack_id', - CREATED, + HTTPStatus.CREATED, None, 'v2', 'apps', 'PUT_{id}_response.json') result = self.client.v2.buildpacks.update('build_pack_id', dict(enabled=False)) diff --git a/test/v2/test_doppler.py b/test/v2/test_doppler.py index 82fe3ae..3324534 100644 --- a/test/v2/test_doppler.py +++ b/test/v2/test_doppler.py @@ -1,9 +1,10 @@ import re import unittest +from functools import reduce +from http import HTTPStatus from abstract_test_case import AbstractTestCase from cloudfoundry_client.doppler.client import DopplerClient -from cloudfoundry_client.imported import OK, reduce from fake_requests import mock_response, TARGET_ENDPOINT @@ -23,7 +24,7 @@ def test_recents(self): boundary = 'd661b2c1426a3abcf1c0524d7fdbc774c42a767bdd6702141702d16047bc' app_guid = 'app_id' self.client.get.return_value = mock_response('/apps/%s/recentlogs' % app_guid, - OK, + HTTPStatus.OK, {'content-type': 'multipart/x-protobuf; boundary=%s' % boundary}, 'recents', 'GET_response.bin') diff --git a/test/v2/test_entities.py b/test/v2/test_entities.py index f303fca..dce4ce2 100644 --- a/test/v2/test_entities.py +++ b/test/v2/test_entities.py @@ -1,9 +1,10 @@ import unittest +from functools import reduce +from http import HTTPStatus +from unittest.mock import MagicMock, call from cloudfoundry_client.v2.entities import EntityManager -from cloudfoundry_client.imported import OK, reduce from fake_requests import TARGET_ENDPOINT, mock_response -from imported import MagicMock, call class TestEntities(unittest.TestCase): @@ -41,11 +42,11 @@ def test_list(self): first_response = mock_response( '/fake/first?order-direction=asc&page=1&results-per-page=20&q=space_guid%3Asome-id', - OK, + HTTPStatus.OK, None, 'fake', 'GET_multi_page_0_response.json') second_response = mock_response('/fake/next?order-direction=asc&page=2&results-per-page=50', - OK, + HTTPStatus.OK, None, 'fake', 'GET_multi_page_1_response.json') @@ -65,7 +66,7 @@ def test_iter(self): client.get.return_value = mock_response( '/fake/something', - OK, + HTTPStatus.OK, None, 'fake', 'GET_response.json') cpt = reduce(lambda increment, _: increment + 1, entity_manager, 0) @@ -79,7 +80,7 @@ def test_get_elem(self): client.get.return_value = mock_response( '/fake/something/with-id', - OK, + HTTPStatus.OK, None, 'fake', 'GET_{id}_response.json') entity = entity_manager['with-id'] diff --git a/test/v2/test_events.py b/test/v2/test_events.py index 220eb16..ffac1ff 100644 --- a/test/v2/test_events.py +++ b/test/v2/test_events.py @@ -1,7 +1,7 @@ import unittest +from http import HTTPStatus from abstract_test_case import AbstractTestCase -from cloudfoundry_client.imported import OK from fake_requests import mock_response @@ -15,7 +15,7 @@ def setUp(self): def test_list(self): self.client.get.return_value = mock_response('/v2/events?q=type%3Aaudit.route.delete-request', - OK, + HTTPStatus.OK, None, 'v2', 'events', 'GET_response_audit.route.delete-request.json') delete_route_events = [event for event in self.client.v2.event.list_by_type('audit.route.delete-request')] diff --git a/test/v2/test_organizations.py b/test/v2/test_organizations.py index bdebf9c..ec5455a 100644 --- a/test/v2/test_organizations.py +++ b/test/v2/test_organizations.py @@ -1,11 +1,12 @@ import sys import unittest +from functools import reduce +from http import HTTPStatus +from unittest.mock import call, patch import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase -from cloudfoundry_client.imported import OK, reduce from fake_requests import mock_response -from imported import patch, call class TestOrganizations(unittest.TestCase, AbstractTestCase): @@ -18,7 +19,7 @@ def setUp(self): def test_list(self): self.client.get.return_value = mock_response('/v2/organizations?q=name%3Aorganization_name', - OK, + HTTPStatus.OK, None, 'v2', 'organizations', 'GET_response.json') cpt = reduce(lambda increment, _: increment + 1, self.client.v2.organizations.list(name='organization_name'), 0) @@ -28,7 +29,7 @@ def test_list(self): def test_get(self): self.client.get.return_value = mock_response( '/v2/organizations/org_id', - OK, + HTTPStatus.OK, None, 'v2', 'organizations', 'GET_{id}_response.json') result = self.client.v2.organizations.get('org_id') @@ -39,12 +40,12 @@ def test_entity(self): self.client.get.side_effect = [ mock_response( '/v2/organizations/org_id', - OK, + HTTPStatus.OK, None, 'v2', 'organizations', 'GET_{id}_response.json'), mock_response( '/v2/organizations/fe79371b-39b8-4f0d-8331-cff423a06aca/spaces', - OK, + HTTPStatus.OK, None, 'v2', 'spaces', 'GET_response.json') ] @@ -59,7 +60,7 @@ def test_main_list_organizations(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.get.return_value = mock_response('/v2/organizations', - OK, + HTTPStatus.OK, None, 'v2', 'organizations', 'GET_response.json') main.main() @@ -70,7 +71,7 @@ def test_main_get_organization(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.get.return_value = mock_response('/v2/organizations/fe79371b-39b8-4f0d-8331-cff423a06aca', - OK, + HTTPStatus.OK, None, 'v2', 'organizations', 'GET_{id}_response.json') main.main() diff --git a/test/v2/test_routes.py b/test/v2/test_routes.py index 1f96555..890da42 100644 --- a/test/v2/test_routes.py +++ b/test/v2/test_routes.py @@ -1,11 +1,12 @@ import sys import unittest +from functools import reduce +from http import HTTPStatus +from unittest.mock import call, patch import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase -from cloudfoundry_client.imported import OK, reduce from fake_requests import mock_response -from imported import patch, call class TestRoutes(unittest.TestCase, AbstractTestCase): @@ -19,7 +20,7 @@ def setUp(self): def test_list(self): self.client.get.return_value = mock_response( '/v2/routes?q=organization_guid%3Aorganization_guid', - OK, + HTTPStatus.OK, None, 'v2', 'routes', 'GET_response.json') cpt = reduce(lambda increment, _: increment + 1, self.client.v2.routes.list(organization_guid='organization_guid'), @@ -30,7 +31,7 @@ def test_list(self): def test_get(self): self.client.get.return_value = mock_response( '/v2/routes/route_id', - OK, + HTTPStatus.OK, None, 'v2', 'routes', 'GET_{id}_response.json') result = self.client.v2.routes.get('route_id') @@ -41,21 +42,21 @@ def test_entity(self): self.client.get.side_effect = [ mock_response( '/v2/routes/route_id', - OK, + HTTPStatus.OK, None, 'v2', 'routes', 'GET_{id}_response.json'), mock_response( '/v2/service_instances/e3db4ea8-ab0c-4c47-adf8-a70a8e990ee4', - OK, + HTTPStatus.OK, None, 'v2', 'service_instances', 'GET_{id}_response.json'), mock_response( '/v2/spaces/b3f94ab9-1520-478b-a6d6-eb467c179ada', - OK, + HTTPStatus.OK, None, 'v2', 'spaces', 'GET_{id}_response.json'), mock_response('/v2/routes/75c16cfe-9b8a-4faf-bb65-02c713c7956f/apps', - OK, + HTTPStatus.OK, None, 'v2', 'apps', 'GET_response.json') ] @@ -72,7 +73,7 @@ def test_main_list_routes(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.get.return_value = mock_response('/v2/routes', - OK, + HTTPStatus.OK, None, 'v2', 'routes', 'GET_response.json') main.main() @@ -83,7 +84,7 @@ def test_main_get_route(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.get.return_value = mock_response('/v2/routes/75c16cfe-9b8a-4faf-bb65-02c713c7956f', - OK, + HTTPStatus.OK, None, 'v2', 'routes', 'GET_{id}_response.json') main.main() diff --git a/test/v2/test_service_bindings.py b/test/v2/test_service_bindings.py index 8b5cb02..73a8903 100644 --- a/test/v2/test_service_bindings.py +++ b/test/v2/test_service_bindings.py @@ -1,11 +1,12 @@ import sys import unittest +from functools import reduce +from http import HTTPStatus +from unittest.mock import call, patch import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase -from cloudfoundry_client.imported import OK, reduce from fake_requests import mock_response -from imported import patch, call, CREATED, NO_CONTENT class TestServiceBindings(unittest.TestCase, AbstractTestCase): @@ -19,7 +20,7 @@ def setUp(self): def test_list(self): self.client.get.return_value = mock_response( '/v2/service_bindings?q=service_instance_guid%3Ainstance_guid', - OK, + HTTPStatus.OK, None, 'v2', 'service_bindings', 'GET_response.json') cpt = reduce(lambda increment, _: increment + 1, @@ -30,7 +31,7 @@ def test_list(self): def test_get(self): self.client.get.return_value = mock_response( '/v2/service_bindings/service_binding_id', - OK, + HTTPStatus.OK, None, 'v2', 'service_bindings', 'GET_{id}_response.json') result = self.client.v2.service_bindings.get('service_binding_id') @@ -40,7 +41,7 @@ def test_get(self): def test_create(self): self.client.post.return_value = mock_response( '/v2/service_bindings', - CREATED, + HTTPStatus.CREATED, None, 'v2', 'service_bindings', 'POST_response.json') service_binding = self.client.v2.service_bindings.create('app_guid', 'instance_guid', @@ -57,7 +58,7 @@ def test_create(self): def test_delete(self): self.client.delete.return_value = mock_response( '/v2/service_bindings/binding_id', - NO_CONTENT, + HTTPStatus.NO_CONTENT, None) self.client.v2.service_bindings.remove('binding_id') self.client.delete.assert_called_with(self.client.delete.return_value.url) @@ -66,17 +67,17 @@ def test_entity(self): self.client.get.side_effect = [ mock_response( '/v2/service_bindings/service_binding_id', - OK, + HTTPStatus.OK, None, 'v2', 'service_bindings', 'GET_{id}_response.json'), mock_response( '/v2/service_instances/ef0bf611-82c6-4603-99fc-3a1a893109d0', - OK, + HTTPStatus.OK, None, 'v2', 'service_instances', 'GET_{id}_response.json'), mock_response( '/v2/apps/c77953c8-6c35-46c7-816e-cf0c42ac2f52', - OK, + HTTPStatus.OK, None, 'v2', 'apps', 'GET_{id}_response.json') ] @@ -91,7 +92,7 @@ def test_main_list_service_bindings(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.get.return_value = mock_response('/v2/service_bindings', - OK, + HTTPStatus.OK, None, 'v2', 'service_bindings', 'GET_response.json') main.main() @@ -102,7 +103,7 @@ def test_main_get_service_binding(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.get.return_value = mock_response('/v2/service_bindings/eaabd042-8f5c-44a2-9580-1e114c36bdcb', - OK, + HTTPStatus.OK, None, 'v2', 'service_bindings', 'GET_{id}_response.json') main.main() diff --git a/test/v2/test_service_brokers.py b/test/v2/test_service_brokers.py index 97c1ead..9082622 100644 --- a/test/v2/test_service_brokers.py +++ b/test/v2/test_service_brokers.py @@ -1,11 +1,12 @@ import sys import unittest +from functools import reduce +from http import HTTPStatus +from unittest.mock import patch import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase -from cloudfoundry_client.imported import OK, reduce from fake_requests import mock_response -from imported import patch, CREATED, NO_CONTENT class TestServiceBrokers(unittest.TestCase, AbstractTestCase): @@ -19,7 +20,7 @@ def setUp(self): def test_list(self): self.client.get.return_value = mock_response( '/v2/service_brokers?q=space_guid%3Aspace_guid', - OK, + HTTPStatus.OK, None, 'v2', 'service_bindings', 'GET_response.json') cpt = reduce(lambda increment, _: increment + 1, @@ -30,7 +31,7 @@ def test_list(self): def test_get(self): self.client.get.return_value = mock_response( '/v2/service_brokers/broker_id', - OK, + HTTPStatus.OK, None, 'v2', 'service_brokers', 'GET_{id}_response.json') result = self.client.v2.service_brokers.get('broker_id') @@ -40,7 +41,7 @@ def test_get(self): def test_create(self): self.client.post.return_value = mock_response( '/v2/service_brokers', - CREATED, + HTTPStatus.CREATED, None, 'v2', 'service_brokers', 'POST_response.json') service_broker = self.client.v2.service_brokers.create('url', 'name', 'username', 'P@sswd1') @@ -54,7 +55,7 @@ def test_create(self): def test_update(self): self.client.put.return_value = mock_response( '/v2/service_brokers/broker_id', - OK, + HTTPStatus.OK, None, 'v2', 'service_brokers', 'PUT_{id}_response.json') service_broker = self.client.v2.service_brokers.update('broker_id', @@ -70,7 +71,7 @@ def test_update(self): def test_delete(self): self.client.delete.return_value = mock_response( '/v2/service_brokers/broker_id', - NO_CONTENT, + HTTPStatus.NO_CONTENT, None) self.client.v2.service_brokers.remove('broker_id') self.client.delete.assert_called_with(self.client.delete.return_value.url) @@ -80,7 +81,7 @@ def test_main_list_service_brokers(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.get.return_value = mock_response('/v2/service_brokers', - OK, + HTTPStatus.OK, None, 'v2', 'service_brokers', 'GET_response.json') main.main() @@ -91,7 +92,7 @@ def test_main_get_service_broker(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.get.return_value = mock_response('/v2/service_brokers/ade9730c-4ee5-4290-ad37-0b15cecd2ca6', - OK, + HTTPStatus.OK, None, 'v2', 'service_brokers', 'GET_{id}_response.json') main.main() diff --git a/test/v2/test_service_instances.py b/test/v2/test_service_instances.py index cf5dd0e..bc74e25 100644 --- a/test/v2/test_service_instances.py +++ b/test/v2/test_service_instances.py @@ -1,11 +1,12 @@ import sys import unittest +from functools import reduce +from http import HTTPStatus +from unittest.mock import call, patch import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase -from cloudfoundry_client.imported import OK, reduce from fake_requests import mock_response -from imported import patch, call, CREATED, NO_CONTENT class TestServiceInstances(unittest.TestCase, AbstractTestCase): @@ -19,7 +20,7 @@ def setUp(self): def test_list(self): self.client.get.return_value = mock_response( '/v2/service_instances?q=service_plan_guid%3Aplan_id&q=space_guid%3Aspace_guid', - OK, + HTTPStatus.OK, None, 'v2', 'service_instances', 'GET_response.json') cpt = reduce(lambda increment, _: increment + 1, @@ -30,7 +31,7 @@ def test_list(self): def test_get(self): self.client.get.return_value = mock_response( '/v2/service_instances/instance_id', - OK, + HTTPStatus.OK, None, 'v2', 'service_instances', 'GET_{id}_response.json') result = self.client.v2.service_instances.get('instance_id') @@ -40,7 +41,7 @@ def test_get(self): def test_create(self): self.client.post.return_value = mock_response( '/v2/service_instances', - CREATED, + HTTPStatus.CREATED, None, 'v2', 'service_instances', 'POST_response.json') service_instance = self.client.v2.service_instances.create('space_guid', 'name', 'plan_id', @@ -62,7 +63,7 @@ def test_create(self): def test_update(self): self.client.put.return_value = mock_response( '/v2/service_instances/instance_id', - OK, + HTTPStatus.OK, None, 'v2', 'service_instances', 'PUT_{id}_response.json') service_instance = self.client.v2.service_instances.update('instance_id', instance_name='new-name', @@ -75,7 +76,7 @@ def test_update(self): def test_update(self): self.client.put.return_value = mock_response( '/v2/service_instances/instance_id', - OK, + HTTPStatus.OK, None, 'v2', 'service_instances', 'PUT_{id}_response.json') service_instance = self.client.v2.service_instances.update('instance_id', instance_name='new-name', @@ -88,86 +89,86 @@ def test_update(self): def test_delete_accepts_incomplete(self): self.client.delete.return_value = mock_response( '/v2/service_instances/instance_id', - NO_CONTENT, + HTTPStatus.NO_CONTENT, None) self.client.v2.service_instances.remove('instance_id', accepts_incomplete=True) self.client.delete.assert_called_with(self.client.delete.return_value.url, - params=dict(accepts_incomplete="true")) + params=dict(accepts_incomplete="true")) self.client.delete.return_value = mock_response( - '/v2/service_instances/instance_id', - NO_CONTENT, - None) + '/v2/service_instances/instance_id', + HTTPStatus.NO_CONTENT, + None) self.client.v2.service_instances.remove('instance_id', accepts_incomplete="true") self.client.delete.assert_called_with(self.client.delete.return_value.url, - params=dict(accepts_incomplete="true")) + params=dict(accepts_incomplete="true")) def test_delete_purge(self): self.client.delete.return_value = mock_response( '/v2/service_instances/instance_id', - NO_CONTENT, + HTTPStatus.NO_CONTENT, None) self.client.v2.service_instances.remove('instance_id', accepts_incomplete=True, purge=True) self.client.delete.assert_called_with(self.client.delete.return_value.url, - params=dict(accepts_incomplete='true', purge="true")) + params=dict(accepts_incomplete='true', purge="true")) self.client.delete.return_value = mock_response( - '/v2/service_instances/instance_id', - NO_CONTENT, - None) + '/v2/service_instances/instance_id', + HTTPStatus.NO_CONTENT, + None) self.client.v2.service_instances.remove('instance_id', purge="true") self.client.delete.assert_called_with(self.client.delete.return_value.url, - params=dict(purge="true")) + params=dict(purge="true")) self.client.delete.return_value = mock_response( - '/v2/service_instances/instance_id', - NO_CONTENT, - None) + '/v2/service_instances/instance_id', + HTTPStatus.NO_CONTENT, + None) self.client.v2.service_instances.remove('instance_id', purge="true") self.client.delete.assert_called_with(self.client.delete.return_value.url, - params=dict(purge="true")) + params=dict(purge="true")) def test_delete(self): self.client.delete.return_value = mock_response( '/v2/service_instances/instance_id', - NO_CONTENT, + HTTPStatus.NO_CONTENT, None) self.client.v2.service_instances.remove('instance_id') self.client.delete.assert_called_with(self.client.delete.return_value.url, - params={}) + params={}) def test_entity(self): self.client.get.side_effect = [ mock_response( '/v2/service_instances/instance_id', - OK, + HTTPStatus.OK, None, 'v2', 'service_instances', 'GET_{id}_response.json'), mock_response( '/v2/spaces/e3138257-8035-4c03-8aba-ab5d35eec0f9', - OK, + HTTPStatus.OK, None, 'v2', 'spaces', 'GET_{id}_response.json') , mock_response( '/v2/service_instances/df52420f-d5b9-4b86-a7d3-6d7005d1ce96/service_bindings', - OK, + HTTPStatus.OK, None, 'v2', 'service_bindings', 'GET_response.json'), mock_response( '/v2/service_plans/65740f84-214a-46cf-b8e3-2233d580f293', - OK, + HTTPStatus.OK, None, 'v2', 'service_plans', 'GET_{id}_response.json'), mock_response( '/v2/service_instances/df52420f-d5b9-4b86-a7d3-6d7005d1ce96/routes', - OK, + HTTPStatus.OK, None, 'v2', 'routes', 'GET_response.json' ), mock_response( '/v2/service_instances/df52420f-d5b9-4b86-a7d3-6d7005d1ce96/service_keys', - OK, + HTTPStatus.OK, None, 'v2', 'service_keys', 'GET_response.json' ) @@ -190,7 +191,7 @@ def test_main_list_service_instances(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.get.return_value = mock_response('/v2/service_instances', - OK, + HTTPStatus.OK, None, 'v2', 'service_instances', 'GET_response.json') main.main() @@ -201,7 +202,7 @@ def test_main_get_service_instance(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.get.return_value = mock_response('/v2/service_instances/df52420f-d5b9-4b86-a7d3-6d7005d1ce96', - OK, + HTTPStatus.OK, None, 'v2', 'service_instances', 'GET_{id}_response.json') main.main() diff --git a/test/v2/test_service_keys.py b/test/v2/test_service_keys.py index 734e22e..79c4177 100644 --- a/test/v2/test_service_keys.py +++ b/test/v2/test_service_keys.py @@ -1,12 +1,13 @@ import json import sys import unittest +from functools import reduce +from http import HTTPStatus +from unittest.mock import patch import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase -from cloudfoundry_client.imported import OK, reduce from fake_requests import mock_response -from imported import patch, CREATED, NO_CONTENT class TestServiceKeys(unittest.TestCase, AbstractTestCase): @@ -20,7 +21,7 @@ def setUp(self): def test_list(self): self.client.get.return_value = mock_response( '/v2/service_keys?q=service_instance_guid%3Ainstance_guid', - OK, + HTTPStatus.OK, None, 'v2', 'service_keys', 'GET_response.json') cpt = reduce(lambda increment, _: increment + 1, @@ -31,7 +32,7 @@ def test_list(self): def test_get(self): self.client.get.return_value = mock_response( '/v2/service_keys/key_id', - OK, + HTTPStatus.OK, None, 'v2', 'service_keys', 'GET_{id}_response.json') result = self.client.v2.service_keys.get('key_id') @@ -41,7 +42,7 @@ def test_get(self): def test_create(self): self.client.post.return_value = mock_response( '/v2/service_keys', - CREATED, + HTTPStatus.CREATED, None, 'v2', 'service_keys', 'POST_response.json') service_key = self.client.v2.service_keys.create('service_instance_id', 'name-127') @@ -54,7 +55,7 @@ def test_create(self): def test_delete(self): self.client.delete.return_value = mock_response( '/v2/service_keys/key_id', - NO_CONTENT, + HTTPStatus.NO_CONTENT, None) self.client.v2.service_keys.remove('key_id') self.client.delete.assert_called_with(self.client.delete.return_value.url) @@ -64,7 +65,7 @@ def test_main_list_service_keys(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.get.return_value = mock_response('/v2/service_keys', - OK, + HTTPStatus.OK, None, 'v2', 'service_keys', 'GET_response.json') main.main() @@ -75,7 +76,7 @@ def test_main_get_service_key(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.get.return_value = mock_response('/v2/service_keys/67755c27-28ed-4087-9688-c07d92f3bcc9', - OK, + HTTPStatus.OK, None, 'v2', 'service_keys', 'GET_{id}_response.json') main.main() @@ -89,7 +90,7 @@ def test_main_create_service_key(self): new=lambda: self.client): self.client.post.return_value = mock_response( '/v2/service_keys', - CREATED, + HTTPStatus.CREATED, None, 'v2', 'service_keys', 'POST_response.json') main.main() @@ -104,7 +105,7 @@ def test_main_delete_service_key(self): new=lambda: self.client): self.client.delete.return_value = mock_response( '/v2/service_keys/67755c27-28ed-4087-9688-c07d92f3bcc9', - NO_CONTENT, + HTTPStatus.NO_CONTENT, None) main.main() self.client.delete.assert_called_with(self.client.delete.return_value.url) diff --git a/test/v2/test_service_plan_visibilities.py b/test/v2/test_service_plan_visibilities.py index 990bfca..2fa7f3b 100644 --- a/test/v2/test_service_plan_visibilities.py +++ b/test/v2/test_service_plan_visibilities.py @@ -1,12 +1,13 @@ import json import sys import unittest +from functools import reduce +from http import HTTPStatus +from unittest.mock import patch import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase -from cloudfoundry_client.imported import OK, reduce from fake_requests import mock_response -from imported import patch, CREATED, NO_CONTENT class TestServicePlanVisibilities(unittest.TestCase, AbstractTestCase): @@ -20,7 +21,7 @@ def setUp(self): def test_list(self): self.client.get.return_value = mock_response( '/v2/service_plan_visibilities?q=space_guid%3Aspace_guid', - OK, + HTTPStatus.OK, None, 'v2', 'service_plan_visibilities', 'GET_response.json') cpt = reduce(lambda increment, _: increment + 1, @@ -31,7 +32,7 @@ def test_list(self): def test_get(self): self.client.get.return_value = mock_response( '/v2/service_plan_visibilities/guid', - OK, + HTTPStatus.OK, None, 'v2', 'service_plan_visibilities', 'GET_{id}_response.json') result = self.client.v2.service_plan_visibilities.get('guid') @@ -41,7 +42,7 @@ def test_get(self): def test_create(self): self.client.post.return_value = mock_response( '/v2/service_plan_visibilities', - CREATED, + HTTPStatus.CREATED, None, 'v2', 'service_plan_visibilities', 'POST_response.json') service_plan_visibilities = self.client.v2.service_plan_visibilities.create('service_plan_guid', @@ -54,7 +55,7 @@ def test_create(self): def test_update(self): self.client.put.return_value = mock_response( '/v2/service_plan_visibilities/guid', - OK, + HTTPStatus.OK, None, 'v2', 'service_plan_visibilities', 'PUT_{id}_response.json') service_plan_visibilities = \ @@ -69,7 +70,7 @@ def test_update(self): def test_delete(self): self.client.delete.return_value = mock_response( '/v2/service_plan_visibilities/guid', - NO_CONTENT, + HTTPStatus.NO_CONTENT, None) self.client.v2.service_plan_visibilities.remove('guid') self.client.delete.assert_called_with(self.client.delete.return_value.url) @@ -82,7 +83,7 @@ def test_main_create_service_plan_visibility(self): new=lambda: self.client): self.client.post.return_value = mock_response( '/v2/service_plan_visibilities', - CREATED, + HTTPStatus.CREATED, None, 'v2', 'service_plan_visibilities', 'POST_response.json') main.main() @@ -96,7 +97,7 @@ def test_main_list_service_plan_visibilities(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.get.return_value = mock_response('/v2/service_plan_visibilities', - OK, + HTTPStatus.OK, None, 'v2', 'service_plan_visibilities', 'GET_response.json') main.main() @@ -108,7 +109,7 @@ def test_main_get_service_plan_visibility(self): new=lambda: self.client): self.client.get.return_value = mock_response( '/v2/service_plan_visibilities/a353104b-1290-418c-bc03-0e647afd0853', - OK, + HTTPStatus.OK, None, 'v2', 'service_plan_visibilities', 'GET_{id}_response.json') main.main() @@ -119,7 +120,7 @@ def test_main_delete_service_plan_visibility(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.delete.return_value = mock_response('/v2/service_plan_visibilities/906775ea-622e-4bc7-af5d-9aab3b652f81', - NO_CONTENT, + HTTPStatus.NO_CONTENT, None) main.main() self.client.delete.assert_called_with(self.client.delete.return_value.url) diff --git a/test/v2/test_service_plans.py b/test/v2/test_service_plans.py index 827519b..2c1fcbd 100644 --- a/test/v2/test_service_plans.py +++ b/test/v2/test_service_plans.py @@ -1,11 +1,12 @@ import sys import unittest +from functools import reduce +from http import HTTPStatus +from unittest.mock import call, patch import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase -from cloudfoundry_client.imported import OK, reduce from fake_requests import mock_response -from imported import patch, call class TestServicePlans(unittest.TestCase, AbstractTestCase): @@ -19,7 +20,7 @@ def setUp(self): def test_list(self): self.client.get.return_value = mock_response( '/v2/service_plans?q=service_guid%3Aservice_id', - OK, + HTTPStatus.OK, None, 'v2', 'service_plans', 'GET_response.json') cpt = reduce(lambda increment, _: increment + 1, self.client.v2.service_plans.list(service_guid='service_id'), 0) @@ -29,7 +30,7 @@ def test_list(self): def test_get(self): self.client.get.return_value = mock_response( '/v2/service_plans/plan_id', - OK, + HTTPStatus.OK, None, 'v2', 'service_plans', 'GET_{id}_response.json') result = self.client.v2.service_plans.get('plan_id') @@ -39,7 +40,7 @@ def test_get(self): def test_list_instances(self): self.client.get.return_value = mock_response( '/v2/service_plans/plan_id/service_instances?q=space_guid%3Aspace_id', - OK, + HTTPStatus.OK, None, 'v2', 'apps', 'GET_{id}_routes_response.json') cpt = reduce(lambda increment, _: increment + 1, @@ -51,18 +52,18 @@ def test_entity(self): self.client.get.side_effect = [ mock_response( '/v2/service_plans/plan_id', - OK, + HTTPStatus.OK, None, 'v2', 'service_plans', 'GET_{id}_response.json'), mock_response( '/v2/services/6a4abae6-93e0-438b-aaa2-5ae67f3a069d', - OK, + HTTPStatus.OK, None, 'v2', 'services', 'GET_{id}_response.json') , mock_response( '/v2/service_plans/5d8f3b0f-6b5b-487f-8fed-4c2d9b812a72/service_instances', - OK, + HTTPStatus.OK, None, 'v2', 'service_instances', 'GET_response.json') ] @@ -79,7 +80,7 @@ def test_main_list_service_plans(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.get.return_value = mock_response('/v2/service_plans', - OK, + HTTPStatus.OK, None, 'v2', 'service_plans', 'GET_response.json') main.main() @@ -90,7 +91,7 @@ def test_main_get_service_plan(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.get.return_value = mock_response('/v2/service_plans/5d8f3b0f-6b5b-487f-8fed-4c2d9b812a72', - OK, + HTTPStatus.OK, None, 'v2', 'service_plans', 'GET_{id}_response.json') main.main() diff --git a/test/v2/test_services.py b/test/v2/test_services.py index e1a4c58..f81333b 100644 --- a/test/v2/test_services.py +++ b/test/v2/test_services.py @@ -1,11 +1,12 @@ import sys import unittest +from functools import reduce +from http import HTTPStatus +from unittest.mock import call, patch import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase -from cloudfoundry_client.imported import OK, reduce from fake_requests import mock_response -from imported import patch, call class TestServices(unittest.TestCase, AbstractTestCase): @@ -18,7 +19,7 @@ def setUp(self): def test_list(self): self.client.get.return_value = mock_response('/v2/services?q=label%3Asome_label', - OK, + HTTPStatus.OK, None, 'v2', 'services', 'GET_response.json') cpt = reduce(lambda increment, _: increment + 1, self.client.v2.services.list(label='some_label'), 0) @@ -28,7 +29,7 @@ def test_list(self): def test_get(self): self.client.get.return_value = mock_response( '/v2/services/service_id', - OK, + HTTPStatus.OK, None, 'v2', 'services', 'GET_{id}_response.json') result = self.client.v2.services.get('service_id') @@ -39,12 +40,12 @@ def test_entity(self): self.client.get.side_effect = [ mock_response( '/v2/services/service_id', - OK, + HTTPStatus.OK, None, 'v2', 'services', 'GET_{id}_response.json'), mock_response( '/v2/services/2c883dbb-a726-4ecf-a0b7-d65588897e7f/service_plans', - OK, + HTTPStatus.OK, None, 'v2', 'service_plans', 'GET_response.json') @@ -60,7 +61,7 @@ def test_main_list_services(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.get.return_value = mock_response('/v2/services', - OK, + HTTPStatus.OK, None, 'v2', 'services', 'GET_response.json') main.main() @@ -71,7 +72,7 @@ def test_main_get_service(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.get.return_value = mock_response('/v2/services/2c883dbb-a726-4ecf-a0b7-d65588897e7f', - OK, + HTTPStatus.OK, None, 'v2', 'services', 'GET_{id}_response.json') main.main() diff --git a/test/v2/test_spaces.py b/test/v2/test_spaces.py index 80e8735..c484834 100644 --- a/test/v2/test_spaces.py +++ b/test/v2/test_spaces.py @@ -1,11 +1,12 @@ import sys import unittest +from functools import reduce +from http import HTTPStatus +from unittest.mock import call, patch import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase -from cloudfoundry_client.imported import OK, reduce from fake_requests import mock_response -from imported import patch, call class TestSpaces(unittest.TestCase, AbstractTestCase): @@ -18,7 +19,7 @@ def setUp(self): def test_list(self): self.client.get.return_value = mock_response('/v2/spaces?q=organization_guid%3Aorg_id', - OK, + HTTPStatus.OK, None, 'v2', 'spaces', 'GET_response.json') cpt = reduce(lambda increment, _: increment + 1, self.client.v2.spaces.list(organization_guid='org_id'), 0) @@ -28,7 +29,7 @@ def test_list(self): def test_get(self): self.client.get.return_value = mock_response( '/v2/spaces/space_id', - OK, + HTTPStatus.OK, None, 'v2', 'spaces', 'GET_{id}_response.json') result = self.client.v2.spaces.get('space_id') @@ -39,22 +40,22 @@ def test_entity(self): self.client.get.side_effect = [ mock_response( '/v2/spaces/space_id', - OK, + HTTPStatus.OK, None, 'v2', 'spaces', 'GET_{id}_response.json'), mock_response( '/v2/organizations/d7d77408-a250-45e3-8de5-71fcf199bbab', - OK, + HTTPStatus.OK, None, 'v2', 'organizations', 'GET_{id}_response.json'), mock_response( '/v2/spaces/2d745a4b-67e3-4398-986e-2adbcf8f7ec9/apps', - OK, + HTTPStatus.OK, None, 'v2', 'apps', 'GET_response.json'), mock_response( '/v2/spaces/2d745a4b-67e3-4398-986e-2adbcf8f7ec9/service_instances', - OK, + HTTPStatus.OK, None, 'v2', 'service_instances', 'GET_response.json') ] @@ -72,7 +73,7 @@ def test_main_list_spaces(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.get.return_value = mock_response('/v2/spaces', - OK, + HTTPStatus.OK, None, 'v2', 'spaces', 'GET_response.json') main.main() @@ -83,7 +84,7 @@ def test_main_get_spaces(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.get.return_value = mock_response('/v2/spaces/2d745a4b-67e3-4398-986e-2adbcf8f7ec9', - OK, + HTTPStatus.OK, None, 'v2', 'spaces', 'GET_{id}_response.json') main.main() diff --git a/test/v3/test_apps.py b/test/v3/test_apps.py index 0048d53..d57bcc4 100644 --- a/test/v3/test_apps.py +++ b/test/v3/test_apps.py @@ -1,10 +1,9 @@ import unittest +from http import HTTPStatus from abstract_test_case import AbstractTestCase -from cloudfoundry_client.imported import OK from cloudfoundry_client.v3.entities import Entity from fake_requests import mock_response -from imported import call, NO_CONTENT class TestApps(unittest.TestCase, AbstractTestCase): @@ -17,7 +16,7 @@ def setUp(self): def test_list(self): self.client.get.return_value = mock_response('/v3/apps', - OK, + HTTPStatus.OK, None, 'v3', 'apps', 'GET_response.json') all_applications = [application for application in self.client.v3.apps.list()] @@ -28,7 +27,7 @@ def test_list(self): def test_get(self): self.client.get.return_value = mock_response('/v3/apps/app_id', - OK, + HTTPStatus.OK, None, 'v3', 'apps', 'GET_{id}_response.json') application = self.client.v3.apps.get('app_id') @@ -37,8 +36,8 @@ def test_get(self): self.assertIsInstance(application, Entity) def test_get_then_space(self): - get_app = mock_response('/v3/apps/app_id', OK, None, 'v3', 'apps', 'GET_{id}_response.json') - get_space = mock_response('/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576', OK, None, + get_app = mock_response('/v3/apps/app_id', HTTPStatus.OK, None, 'v3', 'apps', 'GET_{id}_response.json') + get_space = mock_response('/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576', HTTPStatus.OK, None, 'v3', 'spaces', 'GET_{id}_response.json') self.client.get.side_effect = [ get_app, @@ -51,10 +50,10 @@ def test_get_then_space(self): self.assertEqual("my-space", space['name']) def test_get_then_start(self): - self.client.get.return_value = mock_response('/v3/apps/app_id', OK, None, + self.client.get.return_value = mock_response('/v3/apps/app_id', HTTPStatus.OK, None, 'v3', 'apps', 'GET_{id}_response.json') - self.client.post.return_value = mock_response('/v3/apps/app_id/actions/start', OK, None, - 'v3', 'apps', 'POST_{id}_actions_start_response.json') + self.client.post.return_value = mock_response('/v3/apps/app_id/actions/start', HTTPStatus.OK, None, + 'v3', 'apps', 'POST_{id}_actions_start_response.json') app = self.client.v3.apps.get('app_id').start() self.client.get.assert_called_with(self.client.get.return_value.url) @@ -64,6 +63,6 @@ def test_get_then_start(self): def test_remove(self): self.client.delete.return_value = mock_response( - '/v3/apps/app_id', NO_CONTENT, None) + '/v3/apps/app_id', HTTPStatus.NO_CONTENT, None) self.client.v3.apps.remove('app_id') self.client.delete.assert_called_with(self.client.delete.return_value.url) diff --git a/test/v3/test_buildpacks.py b/test/v3/test_buildpacks.py index 7929ba1..acbc6ed 100644 --- a/test/v3/test_buildpacks.py +++ b/test/v3/test_buildpacks.py @@ -1,13 +1,12 @@ import sys import unittest +from http import HTTPStatus +from unittest.mock import patch import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase -from cloudfoundry_client.imported import OK from cloudfoundry_client.v3.entities import Entity from fake_requests import mock_response -from imported import CREATED, patch -from imported import call, NO_CONTENT class TestBuildpacks(unittest.TestCase, AbstractTestCase): @@ -20,7 +19,7 @@ def setUp(self): def test_list(self): self.client.get.return_value = mock_response('/v3/buildpacks', - OK, + HTTPStatus.OK, None, 'v3', 'buildpacks', 'GET_response.json') all_buildpacks = [buildpack for buildpack in self.client.v3.buildpacks.list()] @@ -32,7 +31,7 @@ def test_list(self): def test_get(self): self.client.get.return_value = mock_response( '/v3/buildpacks/buildpack_id', - OK, + HTTPStatus.OK, None, 'v3', 'buildpacks', 'GET_{id}_response.json') result = self.client.v3.buildpacks.get('buildpack_id') @@ -42,7 +41,7 @@ def test_get(self): def test_update(self): self.client.patch.return_value = mock_response( '/v3/buildpacks/buildpack_id', - OK, + HTTPStatus.OK, None, 'v3', 'buildpacks', 'PATCH_{id}_response.json') result = self.client.v3.buildpacks.update('buildpack_id', 'ruby_buildpack', @@ -65,7 +64,7 @@ def test_update(self): def test_remove(self): self.client.delete.return_value = mock_response( '/v3/buildpacks/buildpack_id', - NO_CONTENT, + HTTPStatus.NO_CONTENT, None) self.client.v3.buildpacks.remove('buildpack_id') self.client.delete.assert_called_with(self.client.delete.return_value.url) @@ -75,7 +74,7 @@ def test_main_list_buildpacks(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.get.return_value = mock_response('/v3/buildpacks', - OK, + HTTPStatus.OK, None, 'v3', 'buildpacks', 'GET_response.json') main.main() @@ -86,7 +85,7 @@ def test_main_get_buildpack(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.get.return_value = mock_response('/v3/buildpacks/6e72c33b-dff0-4020-8603-bcd8a4eb05e4', - OK, + HTTPStatus.OK, None, 'v3', 'buildpacks', 'GET_{id}_response.json') main.main() diff --git a/test/v3/test_organizations.py b/test/v3/test_organizations.py index 4665ab4..4519134 100644 --- a/test/v3/test_organizations.py +++ b/test/v3/test_organizations.py @@ -1,7 +1,7 @@ import unittest +from http import HTTPStatus from abstract_test_case import AbstractTestCase -from cloudfoundry_client.imported import OK from cloudfoundry_client.v3.entities import Entity from fake_requests import mock_response @@ -16,7 +16,7 @@ def setUp(self): def test_list(self): self.client.get.return_value = mock_response('/v3/organizations', - OK, + HTTPStatus.OK, None, 'v3', 'organizations', 'GET_response.json') all_organizations = [organization for organization in self.client.v3.organizations.list()] @@ -27,7 +27,7 @@ def test_list(self): def test_get(self): self.client.get.return_value = mock_response('/v3/organizations/organization_id', - OK, + HTTPStatus.OK, None, 'v3', 'organizations', 'GET_{id}_response.json') organization = self.client.v3.organizations.get('organization_id') diff --git a/test/v3/test_service_instances.py b/test/v3/test_service_instances.py index 4e1059a..b6a58ed 100644 --- a/test/v3/test_service_instances.py +++ b/test/v3/test_service_instances.py @@ -1,7 +1,7 @@ import unittest +from http import HTTPStatus from abstract_test_case import AbstractTestCase -from cloudfoundry_client.imported import OK from cloudfoundry_client.v3.entities import Entity from fake_requests import mock_response @@ -16,7 +16,7 @@ def setUp(self): def test_list(self): self.client.get.return_value = mock_response('/v3/service_instances', - OK, + HTTPStatus.OK, None, 'v3', 'service_instances', 'GET_response.json') all_service_instances = [service_instance for service_instance in self.client.v3.service_instances.list()] @@ -27,7 +27,7 @@ def test_list(self): def test_get(self): self.client.get.return_value = mock_response('/v3/service_instances/service_instance_id', - OK, + HTTPStatus.OK, None, 'v3', 'service_instances', 'GET_{id}_response.json') service_instance = self.client.v3.service_instances.get('service_instance_id') diff --git a/test/v3/test_spaces.py b/test/v3/test_spaces.py index bb07931..3d66e55 100644 --- a/test/v3/test_spaces.py +++ b/test/v3/test_spaces.py @@ -1,10 +1,10 @@ import unittest +from http import HTTPStatus +from unittest.mock import call from abstract_test_case import AbstractTestCase -from cloudfoundry_client.imported import OK from cloudfoundry_client.v3.entities import Entity from fake_requests import mock_response -from imported import call, NO_CONTENT class TestSpaces(unittest.TestCase, AbstractTestCase): @@ -17,7 +17,7 @@ def setUp(self): def test_list(self): self.client.get.return_value = mock_response('/v3/spaces', - OK, + HTTPStatus.OK, None, 'v3', 'spaces', 'GET_response.json') all_spaces = [space for space in self.client.v3.spaces.list()] @@ -28,7 +28,7 @@ def test_list(self): def test_get(self): self.client.get.return_value = mock_response('/v3/spaces/space_id', - OK, + HTTPStatus.OK, None, 'v3', 'spaces', 'GET_{id}_response.json') space = self.client.v3.spaces.get('space_id') @@ -37,9 +37,9 @@ def test_get(self): self.assertIsInstance(space, Entity) def test_get_then_space(self): - get_space = mock_response('/v3/spaces/space_id', OK, None, + get_space = mock_response('/v3/spaces/space_id', HTTPStatus.OK, None, 'v3', 'spaces', 'GET_{id}_response.json') - get_organization = mock_response('/v3/organizations/e00705b9-7b42-4561-ae97-2520399d2133', OK, None, + get_organization = mock_response('/v3/organizations/e00705b9-7b42-4561-ae97-2520399d2133', HTTPStatus.OK, None, 'v3', 'organizations', 'GET_{id}_response.json') self.client.get.side_effect = [ get_space, diff --git a/test/v3/test_tasks.py b/test/v3/test_tasks.py index e4fed9c..b457797 100644 --- a/test/v3/test_tasks.py +++ b/test/v3/test_tasks.py @@ -1,12 +1,12 @@ import sys import unittest +from http import HTTPStatus +from unittest.mock import patch from abstract_test_case import AbstractTestCase -from cloudfoundry_client.imported import OK from cloudfoundry_client.main import main from cloudfoundry_client.v3.entities import Entity from fake_requests import mock_response -from imported import patch, CREATED, ACCEPTED class TestTasks(unittest.TestCase, AbstractTestCase): @@ -19,7 +19,7 @@ def setUp(self): def test_list(self): self.client.get.return_value = mock_response('/v3/tasks', - OK, + HTTPStatus.OK, None, 'v3', 'tasks', 'GET_response.json') all_tasks = [task for task in self.client.v3.tasks.list()] @@ -30,7 +30,7 @@ def test_list(self): def test_get(self): self.client.get.return_value = mock_response('/v3/tasks/task_id', - OK, + HTTPStatus.OK, None, 'v3', 'tasks', 'GET_{id}_response.json') task = self.client.v3.tasks.get('task_id') @@ -41,7 +41,7 @@ def test_get(self): def test_create(self): self.client.post.return_value = mock_response( '/v3/apps/app_guid/tasks', - CREATED, + HTTPStatus.CREATED, None, 'v3', 'tasks', 'POST_response.json') task = self.client.v3.tasks.create('app_guid', command='rake db:migrate') @@ -53,7 +53,7 @@ def test_create(self): def test_cancel(self): self.client.post.return_value = mock_response( '/v3/tasks/task_guid/actions/cancel', - ACCEPTED, + HTTPStatus.ACCEPTED, None, 'v3', 'tasks', 'POST_{id}_actions_cancel_response.json') task = self.client.v3.tasks.cancel('task_guid') @@ -65,7 +65,7 @@ def test_list_tasks(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.get.return_value = mock_response('/v3/tasks?names=task_name', - OK, + HTTPStatus.OK, None, 'v3', 'tasks', 'GET_response.json') main.main() @@ -76,7 +76,7 @@ def test_create_task(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.post.return_value = mock_response('/v3/apps/app_id/tasks', - CREATED, + HTTPStatus.CREATED, None, 'v3', 'tasks', 'POST_response.json') main.main() @@ -89,7 +89,7 @@ def test_cancel_task(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): self.client.post.return_value = mock_response('/v3/tasks/task_id/actions/cancel', - CREATED, + HTTPStatus.CREATED, None, 'v3', 'tasks', 'POST_{id}_actions_cancel_response.json') main.main() From c623ca777cfda7ea700fa3d665e4aab3f00e9619 Mon Sep 17 00:00:00 2001 From: Cooper Date: Thu, 14 Nov 2019 21:16:47 +0100 Subject: [PATCH 002/264] Domains (#62) add domains V3 --- README.rst | 1 + main/cloudfoundry_client/client.py | 2 + main/cloudfoundry_client/main/main.py | 4 + main/cloudfoundry_client/v3/domains.py | 56 +++++++ test/fixtures/v3/domains/GET_response.json | 43 ++++++ .../v3/domains/GET_{id}_response.json | 36 +++++ .../v3/domains/PATCH_{id}_response.json | 36 +++++ test/fixtures/v3/domains/POST_response.json | 36 +++++ ...onships_shared_organizations_response.json | 6 + test/v3/test_buildpacks.py | 24 +++ test/v3/test_domains.py | 140 ++++++++++++++++++ 11 files changed, 384 insertions(+) create mode 100644 main/cloudfoundry_client/v3/domains.py create mode 100644 test/fixtures/v3/domains/GET_response.json create mode 100644 test/fixtures/v3/domains/GET_{id}_response.json create mode 100644 test/fixtures/v3/domains/PATCH_{id}_response.json create mode 100644 test/fixtures/v3/domains/POST_response.json create mode 100644 test/fixtures/v3/domains/POST_{id}_relationships_shared_organizations_response.json create mode 100644 test/v3/test_domains.py diff --git a/README.rst b/README.rst index 0c9d71b..a19facf 100644 --- a/README.rst +++ b/README.rst @@ -199,6 +199,7 @@ Available managers on API V3 are: - ``apps`` - ``buildpacks`` +- ``domains`` - ``organizations`` - ``service_instances`` - ``spaces`` diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 8eeab97..081c4f3 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -21,6 +21,7 @@ from cloudfoundry_client.v2.service_plans import ServicePlanManager from cloudfoundry_client.v3.apps import AppManager as AppManagerV3 from cloudfoundry_client.v3.buildpacks import BuildpackManager as BuildpackManagerV3 +from cloudfoundry_client.v3.domains import DomainManager from cloudfoundry_client.v3.entities import EntityManager as EntityManagerV3 from cloudfoundry_client.v3.tasks import TaskManager @@ -68,6 +69,7 @@ class V3(object): def __init__(self, target_endpoint, credential_manager): self.apps = AppManagerV3(target_endpoint, credential_manager) self.buildpacks = BuildpackManagerV3(target_endpoint, credential_manager) + self.domains = DomainManager(target_endpoint, credential_manager) self.spaces = EntityManagerV3(target_endpoint, credential_manager, '/v3/spaces') self.organizations = EntityManagerV3(target_endpoint, credential_manager, '/v3/organizations') self.service_instances = EntityManagerV3(target_endpoint, credential_manager, '/v3/service_instances') diff --git a/main/cloudfoundry_client/main/main.py b/main/cloudfoundry_client/main/main.py index 434790b..9862671 100644 --- a/main/cloudfoundry_client/main/main.py +++ b/main/cloudfoundry_client/main/main.py @@ -192,6 +192,10 @@ def main(): api_version='v3', filter_list_parameters=[], allow_retrieve_by_name=True, allow_creation=True, allow_deletion=True), + CommandDomain(display_name='Domains', entity_name='domain', + api_version='v3', + filter_list_parameters=[], allow_retrieve_by_name=True, + allow_creation=True, allow_deletion=True), CommandDomain(display_name='Routes', entity_name='route', name_property='host', filter_list_parameters=[]), TaskCommandDomain() ] diff --git a/main/cloudfoundry_client/v3/domains.py b/main/cloudfoundry_client/v3/domains.py new file mode 100644 index 0000000..af91a99 --- /dev/null +++ b/main/cloudfoundry_client/v3/domains.py @@ -0,0 +1,56 @@ +from cloudfoundry_client.v3.entities import EntityManager + + +class DomainManager(EntityManager): + def __init__(self, target_endpoint, client): + super(DomainManager, self).__init__(target_endpoint, client, '/v3/domains') + + def create(self, name, internal=False, organization=None, shared_organizations=None, + meta_labels=None, meta_annotations=None): + data = { + 'name': name, + 'internal': internal, + 'organization': organization, + 'shared_organizations': shared_organizations, + 'metadata': { + 'labels': meta_labels, + 'annotations': meta_annotations + } + } + return super(DomainManager, self)._create(data) + + def list_domains_for_org(self, org_guid, **kwargs): + uri = '/v3/organizations/{guid}/domains'.format(guid=org_guid) + return self._list(uri, **kwargs) + + def update(self, domain_guid, meta_labels=None, meta_annotations=None): + data = { + 'metadata': { + 'labels': meta_labels, + 'annotations': meta_annotations + } + } + return super(DomainManager, self)._update(domain_guid, data) + + def remove(self, domain_guid): + super(DomainManager, self)._remove(domain_guid) + + def __create_shared_domain_url(self, domain_guid): + # TODO use url parser for this + return '{endpoint}{entity}/{domain}/relationships/shared_organizations' \ + ''.format(endpoint=self.target_endpoint, entity=self.entity_uri, + domain=domain_guid) + + def share_domain(self, domain_guid, organization_guids): + if type(organization_guids) is not list(): + organization_guids = [organization_guids] + url = self.__create_shared_domain_url(domain_guid) + data = [] + for org in organization_guids: + data.append({'guid': org}) + return super(DomainManager, self)._post(url, data=data) + + def unshare_domain(self, domain_guid, org_guid): + url = '{uri}/{org}'.format(uri=self.__create_shared_domain_url(domain_guid), + org=org_guid) + super(DomainManager, self)._delete(url) diff --git a/test/fixtures/v3/domains/GET_response.json b/test/fixtures/v3/domains/GET_response.json new file mode 100644 index 0000000..af86245 --- /dev/null +++ b/test/fixtures/v3/domains/GET_response.json @@ -0,0 +1,43 @@ +{ + "pagination": { + "total_results": 3, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/domains?page=1&per_page=2" + }, + "last": { + "href": "https://api.example.org/v3/domains?page=2&per_page=2" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "3a5d3d89-3f89-4f05-8188-8a2b298c79d5", + "created_at": "2019-03-08T01:06:19Z", + "updated_at": "2019-03-08T01:06:19Z", + "name": "test-domain.com", + "internal": false, + "metadata": { + "labels": {}, + "annotations": {} + }, + "relationships": { + "organization": { + "data": null + }, + "shared_organizations": { + "data": [] + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5" + }, + "route_reservations": { + "href": "https://api.example.org/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5/route_reservations" + } + } + } + ] +} \ No newline at end of file diff --git a/test/fixtures/v3/domains/GET_{id}_response.json b/test/fixtures/v3/domains/GET_{id}_response.json new file mode 100644 index 0000000..c8c5e7a --- /dev/null +++ b/test/fixtures/v3/domains/GET_{id}_response.json @@ -0,0 +1,36 @@ +{ + "guid": "3a5d3d89-3f89-4f05-8188-8a2b298c79d5", + "created_at": "2019-03-08T01:06:19Z", + "updated_at": "2019-03-08T01:06:19Z", + "name": "test-domain.com", + "internal": false, + "metadata": { + "labels": { }, + "annotations": { } + }, + "relationships": { + "organization": { + "data": { "guid": "3a3f3d89-3f89-4f05-8188-751b298c79d5" } + }, + "shared_organizations": { + "data": [ + {"guid": "404f3d89-3f89-6z72-8188-751b298d88d5"}, + {"guid": "416d3d89-3f89-8h67-2189-123b298d3592"} + ] + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5" + }, + "organization": { + "href": "https://api.example.org/v3/organizations/3a3f3d89-3f89-4f05-8188-751b298c79d5" + }, + "route_reservations": { + "href": "https://api.example.org/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5/route_reservations" + }, + "shared_organizations": { + "href": "https://api.example.org/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5/relationships/shared_organizations" + } + } +} \ No newline at end of file diff --git a/test/fixtures/v3/domains/PATCH_{id}_response.json b/test/fixtures/v3/domains/PATCH_{id}_response.json new file mode 100644 index 0000000..a762ad3 --- /dev/null +++ b/test/fixtures/v3/domains/PATCH_{id}_response.json @@ -0,0 +1,36 @@ +{ + "guid": "3a5d3d89-3f89-4f05-8188-8a2b298c79d5", + "created_at": "2019-03-08T01:06:19Z", + "updated_at": "2019-03-08T01:06:19Z", + "name": "test-domain.com", + "internal": false, + "metadata": { + "labels": { "key": "value" }, + "annotations": { "note": "detailed information" } + }, + "relationships": { + "organization": { + "data": { "guid": "3a3f3d89-3f89-4f05-8188-751b298c79d5" } + }, + "shared_organizations": { + "data": [ + {"guid": "404f3d89-3f89-6z72-8188-751b298d88d5"}, + {"guid": "416d3d89-3f89-8h67-2189-123b298d3592"} + ] + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5" + }, + "organization": { + "href": "https://api.example.org/v3/organizations/3a3f3d89-3f89-4f05-8188-751b298c79d5" + }, + "route_reservations": { + "href": "https://api.example.org/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5/route_reservations" + }, + "shared_organizations": { + "href": "https://api.example.org/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5/relationships/shared_organizations" + } + } +} \ No newline at end of file diff --git a/test/fixtures/v3/domains/POST_response.json b/test/fixtures/v3/domains/POST_response.json new file mode 100644 index 0000000..c8c5e7a --- /dev/null +++ b/test/fixtures/v3/domains/POST_response.json @@ -0,0 +1,36 @@ +{ + "guid": "3a5d3d89-3f89-4f05-8188-8a2b298c79d5", + "created_at": "2019-03-08T01:06:19Z", + "updated_at": "2019-03-08T01:06:19Z", + "name": "test-domain.com", + "internal": false, + "metadata": { + "labels": { }, + "annotations": { } + }, + "relationships": { + "organization": { + "data": { "guid": "3a3f3d89-3f89-4f05-8188-751b298c79d5" } + }, + "shared_organizations": { + "data": [ + {"guid": "404f3d89-3f89-6z72-8188-751b298d88d5"}, + {"guid": "416d3d89-3f89-8h67-2189-123b298d3592"} + ] + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5" + }, + "organization": { + "href": "https://api.example.org/v3/organizations/3a3f3d89-3f89-4f05-8188-751b298c79d5" + }, + "route_reservations": { + "href": "https://api.example.org/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5/route_reservations" + }, + "shared_organizations": { + "href": "https://api.example.org/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5/relationships/shared_organizations" + } + } +} \ No newline at end of file diff --git a/test/fixtures/v3/domains/POST_{id}_relationships_shared_organizations_response.json b/test/fixtures/v3/domains/POST_{id}_relationships_shared_organizations_response.json new file mode 100644 index 0000000..670f5b2 --- /dev/null +++ b/test/fixtures/v3/domains/POST_{id}_relationships_shared_organizations_response.json @@ -0,0 +1,6 @@ +{ + "data": [ + {"guid": "404f3d89-3f89-6z72-8188-751b298d88d5"}, + {"guid": "416d3d89-3f89-8h67-2189-123b298d3592"} + ] +} diff --git a/test/v3/test_buildpacks.py b/test/v3/test_buildpacks.py index acbc6ed..bb9f540 100644 --- a/test/v3/test_buildpacks.py +++ b/test/v3/test_buildpacks.py @@ -61,6 +61,30 @@ def test_update(self): }) self.assertIsNotNone(result) + def test_create(self): + self.client.post.return_value = mock_response( + '/v3/buildpacks', + HTTPStatus.OK, + None, + 'v3', 'buildpacks', 'POST_response.json') + result = self.client.v3.buildpacks.create('ruby_buildpack', + enabled=True, + position=42, + stack='windows64') + self.client.post.assert_called_with(self.client.post.return_value.url, + files=None, + json={'locked': False, + 'name': 'ruby_buildpack', + 'enabled': True, + 'position': 42, + 'stack': 'windows64', + 'metadata': { + 'labels': None, + 'annotations': None + } + }) + self.assertIsNotNone(result) + def test_remove(self): self.client.delete.return_value = mock_response( '/v3/buildpacks/buildpack_id', diff --git a/test/v3/test_domains.py b/test/v3/test_domains.py new file mode 100644 index 0000000..192d6a1 --- /dev/null +++ b/test/v3/test_domains.py @@ -0,0 +1,140 @@ +import sys +import unittest +from http import HTTPStatus +from unittest.mock import patch + +import cloudfoundry_client.main.main as main +from abstract_test_case import AbstractTestCase +from cloudfoundry_client.v3.entities import Entity +from fake_requests import mock_response + + +class TestDomains(unittest.TestCase, AbstractTestCase): + @classmethod + def setUpClass(cls): + cls.mock_client_class() + + def setUp(self): + self.build_client() + + def test_list(self): + self.client.get.return_value = mock_response('/v3/domains', + HTTPStatus.OK, + None, + 'v3', 'domains', 'GET_response.json') + all_domains = [domain for domain in self.client.v3.domains.list()] + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual(1, len(all_domains)) + self.assertEqual(all_domains[0]['name'], "test-domain.com") + self.assertIsInstance(all_domains[0], Entity) + + def test_get(self): + self.client.get.return_value = mock_response( + '/v3/domains/domain_id', + HTTPStatus.OK, + None, + 'v3', 'domains', 'GET_{id}_response.json') + result = self.client.v3.domains.get('domain_id') + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertIsNotNone(result) + + def test_update(self): + self.client.patch.return_value = mock_response( + '/v3/domains/domain_id', + HTTPStatus.OK, + None, + 'v3', 'domains', 'PATCH_{id}_response.json') + result = self.client.v3.domains.update('domain_id') + self.client.patch.assert_called_with(self.client.patch.return_value.url, + json={'metadata': { + 'labels': None, + 'annotations': None + } + }) + self.assertIsNotNone(result) + + def test_create(self): + self.client.post.return_value = mock_response( + '/v3/domains', + HTTPStatus.OK, + None, + 'v3', 'domains', 'POST_response.json') + result = self.client.v3.domains.create('domain_id', + internal=False, + organization=None, + shared_organizations=None, + meta_labels=None, + meta_annotations=None) + self.client.post.assert_called_with(self.client.post.return_value.url, + files=None, + json={'name': 'domain_id', + 'internal': False, + 'organization': None, + 'shared_organizations': None, + 'metadata': { + 'labels': None, + 'annotations': None + } + }) + self.assertIsNotNone(result) + + def test_remove(self): + self.client.delete.return_value = mock_response( + '/v3/domains/domain_id', + HTTPStatus.NO_CONTENT, + None) + self.client.v3.domains.remove('domain_id') + self.client.delete.assert_called_with(self.client.delete.return_value.url) + + def test_list_domains_for_org(self): + self.client.get.return_value = mock_response('/v3/organizations/org_id/domains', + HTTPStatus.OK, + None, + 'v3', 'domains', 'GET_response.json') + all_domains = [domain for domain in self.client.v3.domains.list_domains_for_org('org_id')] + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual(1, len(all_domains)) + self.assertEqual(all_domains[0]['name'], "test-domain.com") + self.assertIsInstance(all_domains[0], Entity) + + def test_share_domain(self): + self.client.post.return_value = mock_response( + '/v3/domains/domain_id/relationships/shared_organizations', + HTTPStatus.CREATED, + None, + 'v3', 'domains', 'POST_{id}_relationships_shared_organizations_response.json') + result = self.client.v3.domains.share_domain('domain_id', 'org_id') + self.client.post.assert_called_with(self.client.post.return_value.url, + files=None, + json=[{'guid': 'org_id'}]) + self.assertIsNotNone(result) + + def test_unshare_domain(self): + self.client.delete.return_value = mock_response( + '/v3/domains/domain_id/relationships/shared_organizations/org_id', + HTTPStatus.NO_CONTENT, + None) + self.client.v3.domains.unshare_domain('domain_id', 'org_id') + self.client.delete.assert_called_with(self.client.delete.return_value.url) + + @patch.object(sys, 'argv', ['main', 'list_domains']) + def test_main_list_domains(self): + with patch('cloudfoundry_client.main.main.build_client_from_configuration', + new=lambda: self.client): + self.client.get.return_value = mock_response('/v3/domains', + HTTPStatus.OK, + None, + 'v3', 'domains', 'GET_response.json') + main.main() + self.client.get.assert_called_with(self.client.get.return_value.url) + + @patch.object(sys, 'argv', ['main', 'get_domain', '3a5d3d89-3f89-4f05-8188-8a2b298c79d5']) + def test_main_get_domain(self): + with patch('cloudfoundry_client.main.main.build_client_from_configuration', + new=lambda: self.client): + self.client.get.return_value = mock_response('/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5', + HTTPStatus.OK, + None, + 'v3', 'domains', 'GET_{id}_response.json') + main.main() + self.client.get.assert_called_with(self.client.get.return_value.url) From 86e5869d91fc46b125a0bfc3637a19d7b5708a16 Mon Sep 17 00:00:00 2001 From: Cooper Date: Fri, 15 Nov 2019 09:10:48 +0100 Subject: [PATCH 003/264] Feature flag (#63) Add feature flags --- README.rst | 1 + main/cloudfoundry_client/client.py | 2 + main/cloudfoundry_client/v3/feature_flags.py | 13 +++++ .../v3/feature_flags/GET_response.json | 38 +++++++++++++++ .../v3/feature_flags/GET_{id}_response.json | 11 +++++ .../v3/feature_flags/PATCH_{id}_response.json | 11 +++++ test/v3/test_feature_flags.py | 48 +++++++++++++++++++ 7 files changed, 124 insertions(+) create mode 100644 main/cloudfoundry_client/v3/feature_flags.py create mode 100644 test/fixtures/v3/feature_flags/GET_response.json create mode 100644 test/fixtures/v3/feature_flags/GET_{id}_response.json create mode 100644 test/fixtures/v3/feature_flags/PATCH_{id}_response.json create mode 100644 test/v3/test_feature_flags.py diff --git a/README.rst b/README.rst index a19facf..3e36896 100644 --- a/README.rst +++ b/README.rst @@ -200,6 +200,7 @@ Available managers on API V3 are: - ``apps`` - ``buildpacks`` - ``domains`` +- ``feature-flags`` - ``organizations`` - ``service_instances`` - ``spaces`` diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 081c4f3..99c388a 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -23,6 +23,7 @@ from cloudfoundry_client.v3.buildpacks import BuildpackManager as BuildpackManagerV3 from cloudfoundry_client.v3.domains import DomainManager from cloudfoundry_client.v3.entities import EntityManager as EntityManagerV3 +from cloudfoundry_client.v3.feature_flags import FeatureFlagManager from cloudfoundry_client.v3.tasks import TaskManager _logger = logging.getLogger(__name__) @@ -70,6 +71,7 @@ def __init__(self, target_endpoint, credential_manager): self.apps = AppManagerV3(target_endpoint, credential_manager) self.buildpacks = BuildpackManagerV3(target_endpoint, credential_manager) self.domains = DomainManager(target_endpoint, credential_manager) + self.feature_flags = FeatureFlagManager(target_endpoint, credential_manager) self.spaces = EntityManagerV3(target_endpoint, credential_manager, '/v3/spaces') self.organizations = EntityManagerV3(target_endpoint, credential_manager, '/v3/organizations') self.service_instances = EntityManagerV3(target_endpoint, credential_manager, '/v3/service_instances') diff --git a/main/cloudfoundry_client/v3/feature_flags.py b/main/cloudfoundry_client/v3/feature_flags.py new file mode 100644 index 0000000..2b94163 --- /dev/null +++ b/main/cloudfoundry_client/v3/feature_flags.py @@ -0,0 +1,13 @@ +from cloudfoundry_client.v3.entities import EntityManager + + +class FeatureFlagManager(EntityManager): + def __init__(self, target_endpoint, client): + super(FeatureFlagManager, self).__init__(target_endpoint, client, '/v3/feature_flags') + + def update(self, name, enabled=True, custom_error_message=None): + data = { + 'enabled': enabled, + 'custom_error_message': custom_error_message + } + return super(FeatureFlagManager, self)._update(name, data) diff --git a/test/fixtures/v3/feature_flags/GET_response.json b/test/fixtures/v3/feature_flags/GET_response.json new file mode 100644 index 0000000..12cc15f --- /dev/null +++ b/test/fixtures/v3/feature_flags/GET_response.json @@ -0,0 +1,38 @@ +{ + "pagination": { + "total_results": 3, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/feature_flags?page=1&per_page=2" + }, + "last": { + "href": "https://api.example.org/v3/feature_flags?page=2&per_page=2" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "name": "my_feature_flag", + "enabled": true, + "updated_at": "2016-10-17T20:00:42Z", + "custom_error_message": "error message the user sees", + "links": { + "self": { + "href": "https://api.example.org/v3/feature_flags/my_feature_flag" + } + } + }, + { + "name": "my_second_feature_flag", + "enabled": false, + "updated_at": null, + "custom_error_message": null, + "links": { + "self": { + "href": "https://api.example.org/v3/feature_flags/my_second_feature_flag" + } + } + } + ] +} diff --git a/test/fixtures/v3/feature_flags/GET_{id}_response.json b/test/fixtures/v3/feature_flags/GET_{id}_response.json new file mode 100644 index 0000000..c62bb80 --- /dev/null +++ b/test/fixtures/v3/feature_flags/GET_{id}_response.json @@ -0,0 +1,11 @@ +{ + "name": "my_feature_flag", + "enabled": true, + "updated_at": "2016-10-17T20:00:42Z", + "custom_error_message": "error message the user sees", + "links": { + "self": { + "href": "https://api.example.org/v3/feature_flags/my_feature_flag" + } + } +} \ No newline at end of file diff --git a/test/fixtures/v3/feature_flags/PATCH_{id}_response.json b/test/fixtures/v3/feature_flags/PATCH_{id}_response.json new file mode 100644 index 0000000..c62bb80 --- /dev/null +++ b/test/fixtures/v3/feature_flags/PATCH_{id}_response.json @@ -0,0 +1,11 @@ +{ + "name": "my_feature_flag", + "enabled": true, + "updated_at": "2016-10-17T20:00:42Z", + "custom_error_message": "error message the user sees", + "links": { + "self": { + "href": "https://api.example.org/v3/feature_flags/my_feature_flag" + } + } +} \ No newline at end of file diff --git a/test/v3/test_feature_flags.py b/test/v3/test_feature_flags.py new file mode 100644 index 0000000..b6ff46f --- /dev/null +++ b/test/v3/test_feature_flags.py @@ -0,0 +1,48 @@ +import unittest +from http import HTTPStatus + +from abstract_test_case import AbstractTestCase +from fake_requests import mock_response + + +class TestFeatureFlags(unittest.TestCase, AbstractTestCase): + @classmethod + def setUpClass(cls): + cls.mock_client_class() + + def setUp(self): + self.build_client() + + def test_list(self): + self.client.get.return_value = mock_response('/v3/feature_flags', + HTTPStatus.OK, + None, + 'v3', 'feature_flags', 'GET_response.json') + all_feature_flags = [feature_flag for feature_flag in self.client.v3.feature_flags.list()] + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual(2, len(all_feature_flags)) + self.assertEqual(all_feature_flags[0]['name'], "my_feature_flag") + self.assertEqual(all_feature_flags[1]['name'], "my_second_feature_flag") + + def test_get(self): + self.client.get.return_value = mock_response('/v3/feature_flags/feature_flag_name', + HTTPStatus.OK, + None, + 'v3', 'feature_flags', 'GET_{id}_response.json') + feature_flag = self.client.v3.feature_flags.get('feature_flag_name') + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual("my_feature_flag", feature_flag['name']) + + def test_update(self): + self.client.patch.return_value = mock_response( + '/v3/feature_flags/feature_flag_name', + HTTPStatus.OK, + None, + 'v3', 'feature_flags', 'PATCH_{id}_response.json') + result = self.client.v3.feature_flags.update('feature_flag_name') + self.client.patch.assert_called_with(self.client.patch.return_value.url, + json={'enabled': True, + 'custom_error_message': None + } + ) + self.assertIsNotNone(result) From bb16f129db99a6f8cf3f2f8a9869404c85ec6f36 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Fri, 15 Nov 2019 16:02:34 +0100 Subject: [PATCH 004/264] Migrate code to 3.6 compliant code --- main/cloudfoundry_client/client.py | 36 ++++----- main/cloudfoundry_client/doppler/client.py | 13 ++-- .../doppler/websocket_envelope_reader.py | 6 +- .../main/apps_command_domain.py | 18 +++-- .../main/command_domain.py | 73 ++++++++++--------- main/cloudfoundry_client/main/main.py | 28 ++++--- .../main/operation_commands.py | 10 ++- .../main/tasks_command_domain.py | 22 +++--- .../operations/push/cf_ignore.py | 7 +- .../operations/push/file_helper.py | 13 ++-- .../operations/push/push.py | 65 +++++++++-------- .../operations/push/validation/manifest.py | 28 +++---- main/cloudfoundry_client/v2/entities.py | 48 +++++++----- main/cloudfoundry_client/v3/entities.py | 53 +++++++------- requirements.txt | 2 +- 15 files changed, 236 insertions(+), 186 deletions(-) diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 99c388a..77aeffc 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -1,8 +1,10 @@ import logging from http import HTTPStatus +from typing import Optional import requests from oauth2_client.credentials_manager import CredentialManager, ServiceInformation +from requests import Response from cloudfoundry_client.doppler.client import DopplerClient from cloudfoundry_client.errors import InvalidStatusCode @@ -30,7 +32,7 @@ class Info: - def __init__(self, api_version, authorization_endpoint, api_endpoint, doppler_endpoint): + def __init__(self, api_version: str, authorization_endpoint: str, api_endpoint: str, doppler_endpoint: str): self.api_version = api_version self.authorization_endpoint = authorization_endpoint self.api_endpoint = api_endpoint @@ -38,7 +40,7 @@ def __init__(self, api_version, authorization_endpoint, api_endpoint, doppler_en class V2(object): - def __init__(self, target_endpoint, credential_manager): + def __init__(self, target_endpoint: str, credential_manager: 'CloudFoundryClient'): self.apps = AppManagerV2(target_endpoint, credential_manager) self.buildpacks = BuildpackManagerV2(target_endpoint, credential_manager) self.jobs = JobManager(target_endpoint, credential_manager) @@ -67,7 +69,7 @@ def __init__(self, target_endpoint, credential_manager): class V3(object): - def __init__(self, target_endpoint, credential_manager): + def __init__(self, target_endpoint: str, credential_manager: 'CloudFoundryClient'): self.apps = AppManagerV3(target_endpoint, credential_manager) self.buildpacks = BuildpackManagerV3(target_endpoint, credential_manager) self.domains = DomainManager(target_endpoint, credential_manager) @@ -79,7 +81,7 @@ def __init__(self, target_endpoint, credential_manager): class CloudFoundryClient(CredentialManager): - def __init__(self, target_endpoint, client_id='cf', client_secret='', **kwargs): + def __init__(self, target_endpoint: str, client_id: str = 'cf', client_secret: str = '', **kwargs): """" :param target_endpoint :the target endpoint :param client_id: the client_id @@ -116,7 +118,7 @@ def __init__(self, target_endpoint, client_id='cf', client_secret='', **kwargs): self.info = info @property - def doppler(self): + def doppler(self) -> DopplerClient: if self._doppler is None: raise NotImplementedError('No droppler endpoint for this instance') else: @@ -124,7 +126,7 @@ def doppler(self): return self._doppler @staticmethod - def _get_info(target_endpoint, proxy=None, verify=True): + def _get_info(target_endpoint: str, proxy: Optional[dict] = None, verify: bool = True) -> Info: info_response = CloudFoundryClient._check_response(requests.get('%s/v2/info' % target_endpoint, proxies=proxy if proxy is not None else dict( http='', https=''), @@ -136,20 +138,20 @@ def _get_info(target_endpoint, proxy=None, verify=True): info.get('doppler_logging_endpoint')) @staticmethod - def _is_token_expired(response): + def _is_token_expired(response: Response) -> bool: if response.status_code == HTTPStatus.UNAUTHORIZED.value: try: json_data = response.json() result = json_data.get('code', 0) == 1000 and json_data.get('error_code', '') == 'CF-InvalidAuthToken' _logger.info('_is_token_expired - %s' % str(result)) return result - except Exception as _: + except BaseException as _: return False else: return False @staticmethod - def _token_request_headers(_): + def _token_request_headers(_) -> dict: return dict(Accept='application/json') def __getattr__(self, item): @@ -159,7 +161,7 @@ def __getattr__(self, item): else: raise AttributeError("type '%s' has no attribute '%s'" % (type(self).__name__, item)) - def _grant_password_request(self, login, password): + def _grant_password_request(self, login: str, password: str) -> dict: request = super(CloudFoundryClient, self)._grant_password_request(login, password) if self.token_format is not None: request['token_format'] = self.token_format @@ -167,34 +169,34 @@ def _grant_password_request(self, login, password): request['login_hint'] = self.login_hint return request - def _grant_refresh_token_request(self, refresh_token): + def _grant_refresh_token_request(self, refresh_token: str) -> dict: request = super(CloudFoundryClient, self)._grant_refresh_token_request(refresh_token) if self.token_format is not None: request['token_format'] = self.token_format return request - def get(self, url, params=None, **kwargs): + def get(self, url: str, params: Optional[dict] = None, **kwargs) -> Response: response = super(CloudFoundryClient, self).get(url, params, **kwargs) return CloudFoundryClient._check_response(response) - def post(self, url, data=None, json=None, **kwargs): + def post(self, url: str, data=None, json=None, **kwargs) -> Response: response = super(CloudFoundryClient, self).post(url, data, json, **kwargs) return CloudFoundryClient._check_response(response) - def put(self, url, data=None, json=None, **kwargs): + def put(self, url: str, data=None, json=None, **kwargs) -> Response: response = super(CloudFoundryClient, self).put(url, data, json, **kwargs) return CloudFoundryClient._check_response(response) - def patch(self, url, data=None, json=None, **kwargs): + def patch(self, url: str, data=None, json=None, **kwargs) -> Response: response = super(CloudFoundryClient, self).patch(url, data, json, **kwargs) return CloudFoundryClient._check_response(response) - def delete(self, url, **kwargs): + def delete(self, url: str, **kwargs) -> Response: response = super(CloudFoundryClient, self).delete(url, **kwargs) return CloudFoundryClient._check_response(response) @staticmethod - def _check_response(response): + def _check_response(response: Response) -> Response: if int(response.status_code / 100) == 2: return response else: diff --git a/main/cloudfoundry_client/doppler/client.py b/main/cloudfoundry_client/doppler/client.py index c2074a8..ec867e6 100644 --- a/main/cloudfoundry_client/doppler/client.py +++ b/main/cloudfoundry_client/doppler/client.py @@ -2,6 +2,9 @@ import re from urllib.parse import urlparse +from oauth2_client.credentials_manager import CredentialManager +from requests import Response + from cloudfoundry_client.doppler.websocket_envelope_reader import WebsocketFrameReader from cloudfoundry_client.dropsonde.envelope_pb2 import Envelope from cloudfoundry_client.errors import InvalidLogResponseException @@ -10,7 +13,7 @@ class DopplerClient(object): - def __init__(self, doppler_endpoint, proxy, verify_ssl, credentials_manager): + def __init__(self, doppler_endpoint: str, proxy: dict, verify_ssl: bool, credentials_manager: CredentialManager): self.proxy_host = None self.proxy_port = None self.ws_doppler_endpoint = doppler_endpoint @@ -24,7 +27,7 @@ def __init__(self, doppler_endpoint, proxy, verify_ssl, credentials_manager): self.proxy_host = proxy_domain[:idx] self.proxy_port = int(proxy_domain[idx + 1:]) - def recent_logs(self, app_guid): + def recent_logs(self, app_guid: str): url = '%s/apps/%s/recentlogs' % (self.http_doppler_endpoint, app_guid) response = self.credentials_manager.get(url, stream=True) boundary = DopplerClient._extract_boundary(response) @@ -32,7 +35,7 @@ def recent_logs(self, app_guid): for part in DopplerClient._read_multi_part_response(response, boundary): yield DopplerClient._parse_envelope(part) - def stream_logs(self, app_guid): + def stream_logs(self, app_guid: str): url = '%s/apps/%s/stream' % (self.ws_doppler_endpoint, app_guid) with WebsocketFrameReader(url, lambda: self.credentials_manager._access_token, @@ -42,13 +45,13 @@ def stream_logs(self, app_guid): yield DopplerClient._parse_envelope(message) @staticmethod - def _parse_envelope(raw): + def _parse_envelope(raw) -> Envelope: envelope = Envelope() envelope.ParseFromString(raw) return envelope @staticmethod - def _extract_boundary(response): + def _extract_boundary(response: Response) -> str: content_type = response.headers['content-type'] _logger.debug('content-type=%s' % content_type) boundary_field = 'boundary=' diff --git a/main/cloudfoundry_client/doppler/websocket_envelope_reader.py b/main/cloudfoundry_client/doppler/websocket_envelope_reader.py index f96cd12..7be5743 100644 --- a/main/cloudfoundry_client/doppler/websocket_envelope_reader.py +++ b/main/cloudfoundry_client/doppler/websocket_envelope_reader.py @@ -1,10 +1,12 @@ import ssl +from typing import Callable, Optional import websocket class WebsocketFrameReader(object): - def __init__(self, url, access_token_provider, verify_ssl=True, proxy_host=None, proxy_port=None): + def __init__(self, url, access_token_provider: Callable[[], str], verify_ssl: bool = True, + proxy_host: Optional[str] = None, proxy_port: Optional[int] = None): if not verify_ssl: self._ws = websocket.WebSocket(sslopt=dict(cert_reqs=ssl.CERT_NONE)) else: @@ -37,4 +39,4 @@ def __iter__(self): for frame in self._ws: yield frame except websocket.WebSocketConnectionClosedException as _: - pass \ No newline at end of file + pass diff --git a/main/cloudfoundry_client/main/apps_command_domain.py b/main/cloudfoundry_client/main/apps_command_domain.py index e3f97a7..c06092e 100644 --- a/main/cloudfoundry_client/main/apps_command_domain.py +++ b/main/cloudfoundry_client/main/apps_command_domain.py @@ -1,3 +1,7 @@ +from argparse import Action, _SubParsersAction, Namespace +from typing import Callable + +from cloudfoundry_client.client import CloudFoundryClient from cloudfoundry_client.main.command_domain import CommandDomain, Command @@ -27,7 +31,7 @@ def __init__(self): (self.app_routes(), 'List the routes(host) of an application')]) - def recent_logs(self): + def recent_logs(self) -> Command: def execute(client, arguments): resource_id = self.resolve_id(arguments.id[0], lambda x: self._get_client_domain(client).get_first(name=x)) for envelope in client.doppler.recent_logs(resource_id): @@ -35,7 +39,7 @@ def execute(client, arguments): return Command('recent_logs', self._generate_id_command_parser('recent_logs'), execute) - def stream_logs(self): + def stream_logs(self) -> Command: def execute(client, arguments): resource_id = self.resolve_id(arguments.id[0], lambda x: self._get_client_domain(client).get_first(name=x)) try: @@ -46,15 +50,15 @@ def execute(client, arguments): return Command('stream_logs', self._generate_id_command_parser('stream_logs'), execute) - def simple_extra_command(self, entry): + def simple_extra_command(self, entry) -> Command: def execute(client, arguments): resource_id = self.resolve_id(arguments.id[0], lambda x: self._get_client_domain(client).get_first(name=x)) print(getattr(self._get_client_domain(client), entry)(resource_id).json(indent=1)) return Command(entry, self._generate_id_command_parser(entry), execute) - def app_routes(self): - def execute(client, arguments): + def app_routes(self) -> Command: + def execute(client: CloudFoundryClient, arguments: Namespace): resource_id = self.resolve_id(arguments.id[0], lambda x: self._get_client_domain(client).get_first(name=x)) for entity in getattr(self._get_client_domain(client), 'list_routes')(resource_id): print('%s - %s' % (entity['metadata']['guid'], entity['entity']['host'])) @@ -62,8 +66,8 @@ def execute(client, arguments): return Command('app_routes', self._generate_id_command_parser('app_routes'), execute) @staticmethod - def _generate_id_command_parser(entry): - def generate_parser(parser): + def _generate_id_command_parser(entry: str) -> Callable[[_SubParsersAction], None]: + def generate_parser(parser: _SubParsersAction): command_parser = parser.add_parser(entry) command_parser.add_argument('id', metavar='ids', type=str, nargs=1, help='The id. Can be UUID or name (first found then)') diff --git a/main/cloudfoundry_client/main/command_domain.py b/main/cloudfoundry_client/main/command_domain.py index 4587e54..4d8f863 100644 --- a/main/cloudfoundry_client/main/command_domain.py +++ b/main/cloudfoundry_client/main/command_domain.py @@ -2,24 +2,30 @@ import json import os import re +from argparse import _SubParsersAction, Namespace from collections import OrderedDict from http import HTTPStatus +from typing import Callable, Any, List +from cloudfoundry_client.client import CloudFoundryClient from cloudfoundry_client.errors import InvalidStatusCode +from cloudfoundry_client.json_object import JsonObject class Command(object): - def __init__(self, entry, generate_parser, execute): + def __init__(self, entry: str, + generate_parser: Callable[[_SubParsersAction], Any], + execute: Callable[[CloudFoundryClient, Namespace], Any]): self.entry = entry self.generate_parser = generate_parser self.execute = execute class CommandDomain(object): - def __init__(self, display_name, entity_name, filter_list_parameters, - api_version='v2', name_property='name', - allow_retrieve_by_name=False, allow_creation=False, allow_deletion=False, - extra_methods=None): + def __init__(self, display_name: str, entity_name: str, filter_list_parameters: list, + api_version: str = 'v2', name_property: str = 'name', + allow_retrieve_by_name: bool = False, allow_creation: bool = False, allow_deletion: bool = False, + extra_methods: list = None): self.display_name = display_name self.client_domain = self.plural(entity_name) self.api_version = api_version @@ -43,7 +49,7 @@ def __init__(self, display_name, entity_name, filter_list_parameters, self.commands[command[0].entry] = command[0] self.extra_description[command[0].entry] = command[1] - def description(self): + def description(self) -> List[str]: description = [' %s' % self.display_name, ' %s : List %ss' % (self._list_entry(), self.entity_name), ' %s : Get a %s by %s' % (self._get_entry(), self.entity_name, @@ -58,37 +64,37 @@ def description(self): description.extend([' %s : %s' % (k, v) for k, v in self.extra_description.items()]) return description - def generate_parser(self, parser): + def generate_parser(self, parser: _SubParsersAction): for command in self.commands.values(): command.generate_parser(parser) - def is_handled(self, action): + def is_handled(self, action: str) -> bool: return action in self.commands - def execute(self, client, action, arguments): + def execute(self, client: CloudFoundryClient, action: str, arguments: Namespace): return self.commands[action].execute(client, arguments) - def _get_client_domain(self, client): + def _get_client_domain(self, client: CloudFoundryClient) -> Any: return getattr(getattr(client, self.api_version), self.client_domain) @staticmethod - def plural(entity_name): + def plural(entity_name: str) -> str: if entity_name.endswith('y') and not (re.match(r'.+[aeiou]y', entity_name)): return '%sies' % entity_name[:len(entity_name) - 1] else: return '%ss' % entity_name @staticmethod - def is_guid(s): + def is_guid(s: str) -> bool: return re.match(r'[\d|a-z]{8}-[\d|a-z]{4}-[\d|a-z]{4}-[\d|a-z]{4}-[\d|a-z]{12}', s.lower()) is not None - def id(self, entity): + def id(self, entity: JsonObject) -> str: if self.api_version == 'v2': return entity['metadata']['guid'] elif self.api_version == 'v3': return entity['guid'] - def resolve_id(self, argument, get_by_name): + def resolve_id(self, argument: str, get_by_name: Callable[[str], JsonObject]) -> str: if CommandDomain.is_guid(argument): return argument elif self.allow_retrieve_by_name: @@ -103,19 +109,19 @@ def resolve_id(self, argument, get_by_name): else: raise ValueError('id: %s: does not allow search by name' % self.client_domain) - def name(self, entity): + def name(self, entity: JsonObject) -> str: if self.api_version == 'v2': return entity['entity'][self.name_property] elif self.api_version == 'v3': return entity[self.name_property] - def find_by_name(self, client, name): + def find_by_name(self, client: CloudFoundryClient, name: str) -> JsonObject: return self._get_client_domain(client).get_first(**{self.name_property: name}) - def create(self): + def create(self) -> Command: entry = self._create_entry() - def execute(client, arguments): + def execute(client: CloudFoundryClient, arguments: Namespace): data = None if os.path.isfile(arguments.entity[0]): with open(arguments.entity[0], 'r') as f: @@ -130,7 +136,7 @@ def execute(client, arguments): raise ValueError('entity: must be either a valid json file path or a json object') print(self._get_client_domain(client)._create(data).json()) - def generate_parser(parser): + def generate_parser(parser: _SubParsersAction): create_parser = parser.add_parser(entry) create_parser.add_argument('entity', metavar='entities', type=str, nargs=1, help='Either a path of the json file containing the %s or a json object or the json %s object' % ( @@ -138,22 +144,23 @@ def generate_parser(parser): return Command(entry, generate_parser, execute) - def delete(self): + def delete(self) -> Command: entry = self._delete_entry() - def execute(client, arguments): + def execute(client: CloudFoundryClient, arguments: Namespace): if self.is_guid(arguments.id[0]): self._get_client_domain(client)._remove(arguments.id[0]) elif self.allow_retrieve_by_name: entity = self.find_by_name(client, arguments.id[0]) if entity is None: - raise InvalidStatusCode(HTTPStatus.NOT_FOUND, '%s with name %s' % (self.client_domain, arguments.id[0])) + raise InvalidStatusCode(HTTPStatus.NOT_FOUND, + '%s with name %s' % (self.client_domain, arguments.id[0])) else: self._get_client_domain(client)._remove(self.id(entity)) else: raise ValueError('id: %s: does not allow search by name' % self.client_domain) - def generate_parser(parser): + def generate_parser(parser: _SubParsersAction): delete_parser = parser.add_parser(entry) delete_parser.add_argument('id', metavar='ids', type=str, nargs=1, help='The id. Can be UUID or name (first found then)' @@ -161,15 +168,15 @@ def generate_parser(parser): return Command(entry, generate_parser, execute) - def get(self): + def get(self) -> Command: entry = self._get_entry() - def execute(client, arguments): + def execute(client: CloudFoundryClient, arguments: Namespace): resource_id = self.resolve_id(arguments.id[0], functools.partial(self.find_by_name, client)) print(self._get_client_domain(client).get(resource_id).json(indent=1)) - def generate_parser(parser): + def generate_parser(parser: _SubParsersAction): get_parser = parser.add_parser(entry) get_parser.add_argument('id', metavar='ids', type=str, nargs=1, help='The id. Can be UUID or name (first found then)' @@ -177,10 +184,10 @@ def generate_parser(parser): return Command(entry, generate_parser, execute) - def list(self): + def list(self) -> Command: entry = self._list_entry() - def execute(client, arguments): + def execute(client: CloudFoundryClient, arguments: Namespace): filter_list = dict() for filter_parameter in self.filter_list_parameters: filter_value = getattr(arguments, filter_parameter) @@ -192,7 +199,7 @@ def execute(client, arguments): else: print(self.id(entity)) - def generate_parser(parser): + def generate_parser(parser: _SubParsersAction): list_parser = parser.add_parser(entry) for filter_parameter in self.filter_list_parameters: list_parser.add_argument('-%s' % filter_parameter, action='store', dest=filter_parameter, type=str, @@ -200,14 +207,14 @@ def generate_parser(parser): return Command(entry, generate_parser, execute) - def _list_entry(self): + def _list_entry(self) -> str: return 'list_%s' % self.plural(self.entity_name) - def _create_entry(self): + def _create_entry(self) -> str: return 'create_%s' % self.entity_name - def _delete_entry(self): + def _delete_entry(self) -> str: return 'delete_%s' % self.entity_name - def _get_entry(self): + def _get_entry(self) -> str: return 'get_%s' % self.entity_name diff --git a/main/cloudfoundry_client/main/main.py b/main/cloudfoundry_client/main/main.py index 9862671..d658d41 100644 --- a/main/cloudfoundry_client/main/main.py +++ b/main/cloudfoundry_client/main/main.py @@ -6,12 +6,14 @@ import re import sys from http import HTTPStatus +from typing import Tuple, Callable, Any from requests.exceptions import ConnectionError from cloudfoundry_client import __version__ from cloudfoundry_client.client import CloudFoundryClient from cloudfoundry_client.errors import InvalidStatusCode +from cloudfoundry_client.json_object import JsonObject from cloudfoundry_client.main.apps_command_domain import AppCommandDomain from cloudfoundry_client.main.command_domain import CommandDomain, Command from cloudfoundry_client.main.operation_commands import generate_push_command @@ -22,7 +24,8 @@ _logger = logging.getLogger(__name__) -def _read_value_from_user(prompt, error_message=None, validator=None, default=''): +def _read_value_from_user(prompt: str, error_message: str = None, validator: Callable[[str], bool] = None, + default: str = '') -> str: while True: sys.stdout.write('%s [%s]: ' % (prompt, default)) sys.stdout.flush() @@ -38,7 +41,7 @@ def _read_value_from_user(prompt, error_message=None, validator=None, default='' sys.stderr.write('\"%s\": %s\n' % (answer_value, error_message)) -def get_user_directory(): +def get_user_directory() -> str: dir_conf = os.path.join(os.path.expanduser('~')) if not os.path.isdir(dir_conf): if os.path.exists(dir_conf): @@ -47,7 +50,7 @@ def get_user_directory(): return dir_conf -def get_config_file(): +def get_config_file() -> str: return os.path.join(get_user_directory(), '.cf_client_python.json') @@ -69,7 +72,7 @@ def import_from_clf_cli(): refresh_token=cf_cli_data['RefreshToken']), indent=2)) -def build_client_from_configuration(previous_configuration=None): +def build_client_from_configuration(previous_configuration: dict = None) -> CloudFoundryClient: config_file = get_config_file() if not os.path.isfile(config_file): target_endpoint = _read_value_from_user('Please enter a target endpoint', @@ -110,11 +113,12 @@ def build_client_from_configuration(previous_configuration=None): return build_client_from_configuration(configuration) -def is_guid(s): +def is_guid(s: str) -> bool: return re.match(r'[\d|a-z]{8}-[\d|a-z]{4}-[\d|a-z]{4}-[\d|a-z]{4}-[\d|a-z]{12}', s.lower()) is not None -def resolve_id(argument, get_by_name, domain_name, allow_search_by_name): +def resolve_id(argument: str, get_by_name: Callable[[str], JsonObject], domain_name: str, + allow_search_by_name: bool) -> str: if is_guid(argument): return argument elif allow_search_by_name: @@ -127,12 +131,12 @@ def resolve_id(argument, get_by_name, domain_name, allow_search_by_name): raise ValueError('id: %s: does not allow search by name' % domain_name) -def log_recent(client, application_guid): +def log_recent(client: CloudFoundryClient, application_guid: str): for envelope in client.doppler.recent_logs(application_guid): _logger.info(envelope) -def stream_logs(client, application_guid): +def stream_logs(client: CloudFoundryClient, application_guid: str): try: for envelope in client.doppler.stream_logs(application_guid): _logger.info(envelope) @@ -140,17 +144,17 @@ def stream_logs(client, application_guid): pass -def _get_v2_client_domain(client, domain): +def _get_v2_client_domain(client: CloudFoundryClient, domain: str) -> Any: return getattr(client.v2, '%ss' % domain) -def generate_oauth_token_command(): +def generate_oauth_token_command() -> Tuple[Command, str]: entry = 'oauth-token' - def generate_parser(parser): + def generate_parser(parser: argparse._SubParsersAction): parser.add_parser(entry) - def execute(client, arguments): + def execute(client: CloudFoundryClient, arguments: argparse.Namespace): token = client._access_token print(token if token is not None else 'No token') diff --git a/main/cloudfoundry_client/main/operation_commands.py b/main/cloudfoundry_client/main/operation_commands.py index 6e39022..b661fb7 100644 --- a/main/cloudfoundry_client/main/operation_commands.py +++ b/main/cloudfoundry_client/main/operation_commands.py @@ -1,18 +1,22 @@ +from argparse import _SubParsersAction, Namespace +from typing import Tuple + +from cloudfoundry_client.client import CloudFoundryClient from cloudfoundry_client.main.command_domain import Command from cloudfoundry_client.operations.push.push import PushOperation -def generate_push_command(): +def generate_push_command() -> Tuple[Command, str]: entry = 'push_app' - def generate_parser(parser): + def generate_parser(parser: _SubParsersAction): command_parser = parser.add_parser(entry) command_parser.add_argument('manifest_path', metavar='manifest_paths', type=str, nargs=1, help='The manifest path') command_parser.add_argument('-space_guid', action='store', dest='space_guid', type=str, help='Space guid') - def execute(client, arguments): + def execute(client: CloudFoundryClient, arguments: Namespace): manifest_path = arguments.manifest_path[0] PushOperation(client).push(arguments.space_guid, manifest_path) diff --git a/main/cloudfoundry_client/main/tasks_command_domain.py b/main/cloudfoundry_client/main/tasks_command_domain.py index f5311d8..d50fd8a 100644 --- a/main/cloudfoundry_client/main/tasks_command_domain.py +++ b/main/cloudfoundry_client/main/tasks_command_domain.py @@ -1,6 +1,9 @@ import json import os +from argparse import Namespace, _SubParsersAction +from cloudfoundry_client.client import CloudFoundryClient +from cloudfoundry_client.json_object import JsonObject from cloudfoundry_client.main.command_domain import CommandDomain, Command @@ -12,20 +15,19 @@ def __init__(self): api_version='v3', allow_creation=True, allow_deletion=False, extra_methods=[(self.cancel(), 'Cancel Task',)]) - @staticmethod - def id(entity): + def id(self, entity: JsonObject) -> str: return entity['guid'] - def name(self, entity): + def name(self, entity: JsonObject) -> str: return entity[self.name_property] - def find_by_name(self, client, name): + def find_by_name(self, client: CloudFoundryClient, name: str): return self._get_client_domain(client).get_first(**{'%ss' % self.name_property: name}) - def create(self): + def create(self) -> Command: entry = self._create_entry() - def execute(client, arguments): + def execute(client: CloudFoundryClient, arguments: Namespace): data = None if os.path.isfile(arguments.entity[0]): with open(arguments.entity[0], 'r') as f: @@ -40,7 +42,7 @@ def execute(client, arguments): raise ValueError('entity: must be either a valid json file path or a json object') print(self._get_client_domain(client).create(arguments.app_id[0], **data).json()) - def generate_parser(parser): + def generate_parser(parser: _SubParsersAction): create_parser = parser.add_parser(entry) create_parser.add_argument('app_id', metavar='ids', type=str, nargs=1, help='The application UUID.') @@ -50,13 +52,13 @@ def generate_parser(parser): return Command(entry, generate_parser, execute) - def cancel(self): + def cancel(self) -> Command: entry = 'cancel_task' - def execute(client, arguments): + def execute(client: CloudFoundryClient, arguments: Namespace): print(self._get_client_domain(client).cancel(arguments.id[0]).json(indent=1)) - def generate_parser(parser): + def generate_parser(parser: _SubParsersAction): command_parser = parser.add_parser(entry) command_parser.add_argument('id', metavar='ids', type=str, nargs=1, help='The task UUID') diff --git a/main/cloudfoundry_client/operations/push/cf_ignore.py b/main/cloudfoundry_client/operations/push/cf_ignore.py index 62d353c..6d2756e 100644 --- a/main/cloudfoundry_client/operations/push/cf_ignore.py +++ b/main/cloudfoundry_client/operations/push/cf_ignore.py @@ -1,12 +1,13 @@ import fnmatch import logging import os +from typing import List _logger = logging.getLogger(__name__) class CfIgnore(object): - def __init__(self, application_path): + def __init__(self, application_path: str): ignore_file_path = os.path.join(application_path, '.cfignore') self.ignore_items = [] if os.path.isfile(ignore_file_path): @@ -14,7 +15,7 @@ def __init__(self, application_path): for line in ignore_file.readlines(): self.ignore_items.extend(self._pattern(line.strip(' \n'))) - def is_entry_ignored(self, relative_file): + def is_entry_ignored(self, relative_file: str) -> bool: def is_relative_file_ignored(cf_ignore_entry): _logger.debug('is_relative_file_ignored - %s - %s', cf_ignore_entry, relative_file) file_path = '/%s' % relative_file \ @@ -26,7 +27,7 @@ def is_relative_file_ignored(cf_ignore_entry): for ignore_item in self.ignore_items]) @staticmethod - def _pattern(pattern): + def _pattern(pattern: str) -> List[str]: if pattern.find('/') < 0: return [pattern, os.path.join('**', pattern)] elif pattern.endswith('/'): diff --git a/main/cloudfoundry_client/operations/push/file_helper.py b/main/cloudfoundry_client/operations/push/file_helper.py index ebef811..d9c187c 100644 --- a/main/cloudfoundry_client/operations/push/file_helper.py +++ b/main/cloudfoundry_client/operations/push/file_helper.py @@ -2,11 +2,12 @@ import os import stat import zipfile +from typing import Callable, Optional, Generator, Tuple, List class FileHelper(object): @staticmethod - def zip(file_location, directory_path, accept=None): + def zip(file_location: str, directory_path: str, accept: Optional[Callable[[str], bool]] = None): with zipfile.ZipFile(file_location, 'w', zipfile.ZIP_DEFLATED) as archive_out: for dir_path, file_names in FileHelper.walk(directory_path): dir_full_location = os.path.join(directory_path, dir_path) @@ -19,7 +20,7 @@ def zip(file_location, directory_path, accept=None): zipfile.ZIP_DEFLATED) @staticmethod - def unzip(path, tmp_dir): + def unzip(path: str, tmp_dir: str): with zipfile.ZipFile(path, 'r') as zip_ref: for entry in zip_ref.namelist(): filename = os.path.basename(entry) @@ -30,12 +31,12 @@ def unzip(path, tmp_dir): zip_ref.extract(entry, tmp_dir) @staticmethod - def walk(path): + def walk(path: str) -> Generator[Tuple[str, List[str]], None, None]: for dir_path, _, files in os.walk(path, topdown=True): yield dir_path[len(path):].lstrip('/'), files @staticmethod - def sha1(file_location): + def sha1(file_location: str) -> str: sha1 = hashlib.sha1() with open(file_location, 'rb') as f: while True: @@ -46,10 +47,10 @@ def sha1(file_location): return sha1.hexdigest() @staticmethod - def size(path): + def size(path: str) -> int: return os.path.getsize(path) @staticmethod - def mode(file_location): + def mode(file_location: str) -> str: mode = str(oct(stat.S_IMODE(os.lstat(file_location).st_mode))) return mode[len(mode) - 3:] diff --git a/main/cloudfoundry_client/operations/push/push.py b/main/cloudfoundry_client/operations/push/push.py index 770a815..cbe0d27 100644 --- a/main/cloudfoundry_client/operations/push/push.py +++ b/main/cloudfoundry_client/operations/push/push.py @@ -5,10 +5,13 @@ import shutil import tempfile import time +from typing import Tuple, List, Dict, Optional +from cloudfoundry_client.client import CloudFoundryClient from cloudfoundry_client.operations.push.cf_ignore import CfIgnore from cloudfoundry_client.operations.push.file_helper import FileHelper from cloudfoundry_client.operations.push.validation.manifest import ManifestReader +from cloudfoundry_client.v2.entities import Entity _logger = logging.getLogger(__name__) @@ -18,10 +21,10 @@ class PushOperation(object): SPLIT_ROUTE_PATTERN = re.compile('(?P[a-z]+://)?(?P[^:/]+)(?P:\d+)?(?P/.*)?') - def __init__(self, client): + def __init__(self, client: CloudFoundryClient): self.client = client - def push(self, space_id, manifest_path, restart=True): + def push(self, space_id: str, manifest_path: str, restart: bool = True): app_manifests = ManifestReader.load_application_manifests(manifest_path) organization, space = self._retrieve_space_and_organization(space_id) @@ -29,12 +32,12 @@ def push(self, space_id, manifest_path, restart=True): if 'path' in app_manifest or 'docker' in app_manifest: self._push_application(organization, space, app_manifest, restart) - def _retrieve_space_and_organization(self, space_id): + def _retrieve_space_and_organization(self, space_id: str) -> Tuple[Entity, Entity]: space = self.client.v2.spaces.get(space_id) organization = space.organization() return organization, space - def _push_application(self, organization, space, app_manifest, restart): + def _push_application(self, organization: Entity, space: Entity, app_manifest: dict, restart: bool): app = self._init_application(space, app_manifest) self._route_application(organization, space, app, app_manifest.get('no-route', False), app_manifest.get('routes', []), app_manifest.get('random-route', False)) @@ -44,12 +47,12 @@ def _push_application(self, organization, space, app_manifest, restart): if restart: PushOperation._restart_application(app) - def _init_application(self, space, app_manifest): + def _init_application(self, space: Entity, app_manifest: dict) -> Entity: app = self.client.v2.apps.get_first(name=app_manifest['name'], space_guid=space['metadata']['guid']) return self._update_application(app, app_manifest) if app is not None \ else self._create_application(space, app_manifest) - def _create_application(self, space, app_manifest): + def _create_application(self, space: Entity, app_manifest: dict) -> Entity: _logger.debug("Creating application %s", app_manifest['name']) request = self._build_request_from_manifest(app_manifest) request['environment_json'] = PushOperation._merge_environment(None, app_manifest) @@ -58,7 +61,7 @@ def _create_application(self, space, app_manifest): request['health-check-http-endpoint'] = '/' return self.client.v2.apps.create(**request) - def _update_application(self, app, app_manifest): + def _update_application(self, app: Entity, app_manifest: dict) -> Entity: _logger.debug("Uploading application %s", app['entity']['name']) request = self._build_request_from_manifest(app_manifest) request['environment_json'] = PushOperation._merge_environment(app, app_manifest) @@ -67,7 +70,7 @@ def _update_application(self, app, app_manifest): request['health-check-http-endpoint'] = '/' return self.client.v2.apps.update(app['metadata']['guid'], **request) - def _build_request_from_manifest(self, app_manifest): + def _build_request_from_manifest(self, app_manifest: dict) -> dict: request = dict() request.update(app_manifest) stack = self.client.v2.stacks.get_first(name=app_manifest['stack']) if 'stack' in app_manifest else None @@ -85,7 +88,7 @@ def _build_request_from_manifest(self, app_manifest): return request @staticmethod - def _merge_environment(app, app_manifest): + def _merge_environment(app: Optional[Entity], app_manifest: dict) -> dict: environment = dict() if app is not None and 'environment_json' in app['entity']: environment.update(app['entity']['environment_json']) @@ -93,7 +96,8 @@ def _merge_environment(app, app_manifest): environment.update(app_manifest['env']) return environment - def _route_application(self, organization, space, app, no_route, routes, random_route): + def _route_application(self, organization: Entity, space: Entity, app: Entity, no_route: bool, routes: List[str], + random_route: bool): existing_routes = [route for route in app.routes()] if no_route: self._remove_all_routes(app, existing_routes) @@ -102,11 +106,11 @@ def _route_application(self, organization, space, app, no_route, routes, random_ else: self._build_new_requested_routes(organization, space, app, existing_routes, routes) - def _remove_all_routes(self, app, routes): + def _remove_all_routes(self, app: Entity, routes: List[Entity]): for route in routes: self.client.v2.apps.remove_route(app['metadata']['guid'], route['metadata']['guid']) - def _build_default_route(self, space, app, random_route): + def _build_default_route(self, space: Entity, app: Entity, random_route: bool): shared_domain = None for domain in self.client.v2.shared_domains.list(): if not domain['entity'].get('internal', False): @@ -128,7 +132,8 @@ def _build_default_route(self, space, app, random_route): self._to_host(app['entity']['name'])) self.client.v2.apps.associate_route(app['metadata']['guid'], route['metadata']['guid']) - def _build_new_requested_routes(self, organization, space, app, existing_routes, requested_routes): + def _build_new_requested_routes(self, organization: Entity, space: Entity, app: Entity, + existing_routes: List[Entity], requested_routes: List[str]): private_domains = {domain['entity']['name']: domain for domain in organization.private_domains()} shared_domains = {domain['entity']['name']: domain for domain in self.client.v2.shared_domains.list()} for requested_route in requested_routes: @@ -161,7 +166,7 @@ def _build_new_requested_routes(self, organization, space, app, existing_routes, _logger.debug('Associating route %s to application %s', requested_route, app['entity']['name']) self.client.v2.apps.associate_route(app['metadata']['guid'], route_to_map['metadata']['guid']) - def _resolve_new_host_route(self, space, domain, host, path): + def _resolve_new_host_route(self, space: Entity, domain: Entity, host: str, path: str) -> Entity: existing_route = self.client.v2.routes.get_first(domain_guid=domain['metadata']['guid'], host=host, path=path) if existing_route is None: _logger.debug('Creating host route %s on domain %s and path %s', host, domain['entity']['name'], path) @@ -177,7 +182,7 @@ def _resolve_new_host_route(self, space, domain, host, path): existing_route['metadata']['guid']) return existing_route - def _resolve_new_tcp_route(self, space, domain, port): + def _resolve_new_tcp_route(self, space: Entity, domain: Entity, port: int) -> Entity: existing_route = self.client.v2.routes.get_first(domain_guid=domain['metadata']['guid'], port=port) if existing_route is None: _logger.debug('Creating tcp route %d on domain %s', port, domain['entity']['name']) @@ -192,7 +197,7 @@ def _resolve_new_tcp_route(self, space, domain, port): return existing_route @staticmethod - def _split_route(requested_route): + def _split_route(requested_route: str) -> Tuple[str, int, str]: route_splitted = PushOperation.SPLIT_ROUTE_PATTERN.match(requested_route['route']) if route_splitted is None: raise AssertionError('Invalid route: %s' % requested_route['route']) @@ -202,7 +207,8 @@ def _split_route(requested_route): return domain, int(port[1:]) if port is not None else None, '' if path is None or path == '/' else path @staticmethod - def _resolve_domain(route, private_domains, shared_domains): + def _resolve_domain(route: str, private_domains: Dict[str, Entity], shared_domains: Dict[str, Entity]) -> Tuple[ + str, str, Entity]: for domains in [private_domains, shared_domains]: if route in domains: return '', route, domains[route] @@ -215,7 +221,7 @@ def _resolve_domain(route, private_domains, shared_domains): return host, domain, domains[domain] raise AssertionError('Cannot find domain for route %s' % route) - def _upload_application(self, app, application_path): + def _upload_application(self, app: Entity, application_path: str) -> Entity: _logger.debug('Uploading application %s', app['entity']['name']) if os.path.isfile(application_path): self._upload_application_zip(app, application_path) @@ -224,7 +230,7 @@ def _upload_application(self, app, application_path): else: raise AssertionError('Path %s is neither a directory nor a file' % application_path) - def _upload_application_zip(self, app, path): + def _upload_application_zip(self, app: Entity, path: str): _logger.debug('Unzipping file %s', path) tmp_dir = tempfile.mkdtemp() try: @@ -233,13 +239,13 @@ def _upload_application_zip(self, app, path): finally: shutil.rmtree(tmp_dir) - def _upload_application_directory(self, app, application_path): + def _upload_application_directory(self, app: Entity, application_path: str): _logger.debug('Uploading application from directory %s', application_path) _, temp_file = tempfile.mkstemp() try: resource_descriptions_by_path = PushOperation._load_all_resources(application_path) - def generate_key(item): + def generate_key(item: dict): return '%s-%d' % (item["sha1"], item["size"]) already_uploaded_entries = [generate_key(item) for item in @@ -251,7 +257,8 @@ def generate_key(item): FileHelper.zip(temp_file, application_path, lambda item: item in resource_descriptions_by_path - and generate_key(resource_descriptions_by_path[item]) not in already_uploaded_entries) + and generate_key( + resource_descriptions_by_path[item]) not in already_uploaded_entries) _logger.debug('Diff zip file built: %s', temp_file) resources = [ dict(fn=resource_path, @@ -271,7 +278,7 @@ def generate_key(item): _logger.debug('Skipping remove of zip file') @staticmethod - def _load_all_resources(top_directory): + def _load_all_resources(top_directory: str) -> dict: application_items = {} cf_ignore = CfIgnore(top_directory) for directory, file_names in FileHelper.walk(top_directory): @@ -285,7 +292,7 @@ def _load_all_resources(top_directory): mode=FileHelper.mode(absolute_file_location)) return application_items - def _bind_services(self, space, app, services): + def _bind_services(self, space: Entity, app: Entity, services: List[str]): service_instances = [service_instance for service_instance in space.service_instances( return_user_provided_service_instances="true")] service_name_to_instance_guid = {service_instance["entity"]["name"]: service_instance["metadata"]["guid"] @@ -302,7 +309,7 @@ def _bind_services(self, space, app, services): _logger.debug('Binding %s to %s', app["entity"]["name"], service_name) self.client.v2.service_bindings.create(app['metadata']['guid'], service_instance_guid) - def _poll_job(self, job): + def _poll_job(self, job: Entity): def job_not_ended(j): return j['entity']['status'] in ['queued', 'running'] @@ -325,17 +332,17 @@ def job_not_ended(j): _logger.debug('Job ended with status %s', job['entity']['status']) @staticmethod - def _restart_application(app): + def _restart_application(app: Entity): _logger.debug("Restarting application") app.stop() app.start() @staticmethod - def _to_host(host): - def no_space(h): + def _to_host(host: str) -> str: + def no_space(h: str) -> str: return re.sub('[\s_]+', "-", h) - def only_alphabetical_and_hyphen(h): + def only_alphabetical_and_hyphen(h: str) -> str: return re.sub("[^a-z0-9-]", "", h) return only_alphabetical_and_hyphen(no_space(host)) diff --git a/main/cloudfoundry_client/operations/push/validation/manifest.py b/main/cloudfoundry_client/operations/push/validation/manifest.py index b074c2c..ef00724 100644 --- a/main/cloudfoundry_client/operations/push/validation/manifest.py +++ b/main/cloudfoundry_client/operations/push/validation/manifest.py @@ -13,7 +13,7 @@ class ManifestReader(object): BOOLEAN_FIELDS = ['no-route', 'random-route'] @staticmethod - def load_application_manifests(manifest_path): + def load_application_manifests(manifest_path: str): with open(manifest_path, 'r') as fp: manifest = yaml.safe_load(fp) if manifest is None: @@ -22,12 +22,12 @@ def load_application_manifests(manifest_path): return manifest['applications'] @staticmethod - def _validate_manifest(manifest_directory, manifest): + def _validate_manifest(manifest_directory: str, manifest: dict): for app_manifest in manifest['applications']: ManifestReader._validate_application_manifest(manifest_directory, app_manifest) @staticmethod - def _validate_application_manifest(manifest_directory, app_manifest): + def _validate_application_manifest(manifest_directory: str, app_manifest: dict): name = app_manifest.get('name') if name is None or len(name) == 0: raise AssertionError('name must be set') @@ -50,7 +50,7 @@ def _validate_application_manifest(manifest_directory, app_manifest): ManifestReader._validate_routes(app_manifest) @staticmethod - def _check_deprecated_attributes(app_manifest): + def _check_deprecated_attributes(app_manifest: dict): if app_manifest.get('hosts') is not None or app_manifest.get('host') \ or app_manifest.get('domains') is not None or app_manifest.get('domain') \ or app_manifest.get('no-hostname') is not None: @@ -58,7 +58,7 @@ def _check_deprecated_attributes(app_manifest): 'hosts, host, domains, domain and no-hostname are all deprecated. Use the routes attribute') @staticmethod - def _convert_memory(manifest): + def _convert_memory(manifest: dict): if 'memory' in manifest: memory = manifest['memory'].upper() match = ManifestReader.MEMORY_PATTERN.match(memory) @@ -79,7 +79,7 @@ def _convert_memory(manifest): manifest['memory'] = int(memory_number / (1024 * 1024)) @staticmethod - def _convert_positive(manifest, field): + def _convert_positive(manifest: dict, field: str): if field in manifest: value = int(manifest[field]) if value < 1: @@ -87,19 +87,19 @@ def _convert_positive(manifest, field): manifest[field] = value @staticmethod - def _convert_boolean(manifest, field): + def _convert_boolean(manifest: dict, field: str): if field in manifest: field_value = manifest[field] manifest[field] = field_value if type(field_value) == bool else field_value.lower() == 'true' @staticmethod - def _validate_routes(manifest): + def _validate_routes(manifest: dict): for route in manifest.get('routes', []): if type(route) != dict or 'route' not in route: raise AssertionError('routes attribute must be a list of object containing a route attribute') @staticmethod - def _validate_application_docker(docker_manifest): + def _validate_application_docker(docker_manifest: dict): docker_image = docker_manifest.get('image') if docker_image is not None and docker_manifest.get('buildpack') is not None: raise AssertionError('image and buildpack can not both be set for docker') @@ -111,7 +111,7 @@ def _validate_application_docker(docker_manifest): raise AssertionError('Docker image not set while docker username/password are set') @staticmethod - def _absolute_path(manifest_directory, manifest): + def _absolute_path(manifest_directory: str, manifest: dict): if 'path' in manifest: path = manifest['path'] if path == os.path.abspath(path): @@ -122,10 +122,12 @@ def _absolute_path(manifest_directory, manifest): manifest['path'] = os.path.abspath(os.path.join(manifest_directory, path)) @staticmethod - def _convert_environment(app_manifest): + def _convert_environment(app_manifest: dict): environment = app_manifest.get('env', None) if environment is not None: if type(environment) != dict: raise AssertionError("'env' entry must be a dictionary") - app_manifest['env'] = {key: json.dumps(value) for key, value in environment.items() if value is not None and type(value) != str} - app_manifest['env'].update({key: value for key, value in environment.items() if value is not None and type(value) == str}) + app_manifest['env'] = {key: json.dumps(value) for key, value in environment.items() if + value is not None and type(value) != str} + app_manifest['env'].update( + {key: value for key, value in environment.items() if value is not None and type(value) == str}) diff --git a/main/cloudfoundry_client/v2/entities.py b/main/cloudfoundry_client/v2/entities.py index b186b73..a8ae861 100644 --- a/main/cloudfoundry_client/v2/entities.py +++ b/main/cloudfoundry_client/v2/entities.py @@ -1,7 +1,10 @@ import logging from functools import partial, reduce +from typing import Callable, List, Tuple, Any, Optional, Generator from urllib.parse import quote +from requests import Response + from cloudfoundry_client.errors import InvalidEntity from cloudfoundry_client.json_object import JsonObject from cloudfoundry_client.request_object import Request @@ -10,7 +13,7 @@ class Entity(JsonObject): - def __init__(self, target_endpoint, client, *args, **kwargs): + def __init__(self, target_endpoint: str, client: 'CloudFoundryClient', *args, **kwargs): super(Entity, self).__init__(*args, **kwargs) self.target_endpoint = target_endpoint self.client = client @@ -41,25 +44,30 @@ def __init__(self, target_endpoint, client, *args, **kwargs): raise InvalidEntity(**self) +EntityBuilder = Callable[[List[Tuple[str, Any]]], Entity] + + class EntityManager(object): list_query_parameters = ['page', 'results-per-page', 'order-direction'] list_multi_parameters = ['order-by'] - def __init__(self, target_endpoint, client, entity_uri, entity_builder=None): + def __init__(self, target_endpoint: str, client: 'CloudFoundryClient', entity_uri: str, + entity_builder: Optional[EntityBuilder] = None): self.target_endpoint = target_endpoint self.entity_uri = entity_uri self.client = client self.entity_builder = entity_builder if entity_builder is not None else lambda pairs: Entity(target_endpoint, client, pairs) - def _get(self, requested_path, entity_builder=None): + def _get(self, requested_path: str, entity_builder: Optional[EntityBuilder] = None) -> Entity: url = '%s%s' % (self.target_endpoint, requested_path) response = self.client.get(url) _logger.debug('GET - %s - %s', requested_path, response.text) return self._read_response(response, entity_builder) - def _list(self, requested_path, entity_builder=None, **kwargs): + def _list(self, requested_path: str, entity_builder: Optional[EntityBuilder] = None, **kwargs) \ + -> Generator[Entity, None, None]: url_requested = self._get_url_filtered('%s%s' % (self.target_endpoint, requested_path), **kwargs) response = self.client.get(url_requested) entity_builder = self._get_entity_builder(entity_builder) @@ -74,72 +82,72 @@ def _list(self, requested_path, entity_builder=None, **kwargs): url_requested = '%s%s' % (self.target_endpoint, response_json['next_url']) response = self.client.get(url_requested) - def _create(self, data, **kwargs): + def _create(self, data: dict, **kwargs) -> Entity: url = '%s%s' % (self.target_endpoint, self.entity_uri) return self._post(url, data, **kwargs) - def _update(self, resource_id, data, **kwargs): + def _update(self, resource_id: str, data: dict, **kwargs): url = '%s%s/%s' % (self.target_endpoint, self.entity_uri, resource_id) return self._put(url, data, **kwargs) - def _remove(self, resource_id, **kwargs): + def _remove(self, resource_id: str, **kwargs): url = '%s%s/%s' % (self.target_endpoint, self.entity_uri, resource_id) self._delete(url, **kwargs) - def _post(self, url, data=None, **kwargs): + def _post(self, url: str, data: Optional[dict] = None, **kwargs): response = self.client.post(url, json=data, **kwargs) _logger.debug('POST - %s - %s', url, response.text) return self._read_response(response) - def _put(self, url, data=None, **kwargs): + def _put(self, url: str, data: Optional[dict] = None, **kwargs): response = self.client.put(url, json=data, **kwargs) _logger.debug('PUT - %s - %s', url, response.text) return self._read_response(response) - def _delete(self, url, **kwargs): + def _delete(self, url: str, **kwargs): response = self.client.delete(url, **kwargs) _logger.debug('DELETE - %s - %s', url, response.text) - def __iter__(self): + def __iter__(self) -> Generator[Entity, None, None]: return self.list() - def __getitem__(self, entity_guid): + def __getitem__(self, entity_guid) -> Entity: return self.get(entity_guid) - def list(self, **kwargs): + def list(self, **kwargs) -> Generator[Entity, None, None]: return self._list(self.entity_uri, **kwargs) - def get_first(self, **kwargs): + def get_first(self, **kwargs) -> Optional[Entity]: kwargs.setdefault('results-per-page', 1) for entity in self._list(self.entity_uri, **kwargs): return entity return None - def get(self, entity_id, *extra_paths): + def get(self, entity_id: str, *extra_paths) -> Entity: if len(extra_paths) == 0: requested_path = '%s/%s' % (self.entity_uri, entity_id) else: requested_path = '%s/%s/%s' % (self.entity_uri, entity_id, '/'.join(extra_paths)) return self._get(requested_path) - def _read_response(self, response, other_entity_builder=None): + def _read_response(self, response: Response, other_entity_builder: Optional[EntityBuilder] = None): entity_builder = self._get_entity_builder(other_entity_builder) result = response.json(object_pairs_hook=JsonObject) return entity_builder(list(result.items())) @staticmethod - def _request(**mandatory_parameters): + def _request(**mandatory_parameters) -> Request: return Request(**mandatory_parameters) - def _get_entity_builder(self, entity_builder): + def _get_entity_builder(self, entity_builder: Optional[EntityBuilder]) -> EntityBuilder: if entity_builder is None: return self.entity_builder else: return entity_builder - def _get_url_filtered(self, url, **kwargs): + def _get_url_filtered(self, url: str, **kwargs) -> str: - def _append_encoded_parameter(parameters, args): + def _append_encoded_parameter(parameters: List[str], args: Tuple[str, Any]) -> List[str]: parameter_name, parameter_value = args[0], args[1] if parameter_name in self.list_query_parameters: parameters.append('%s=%s' % (parameter_name, str(parameter_value))) diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index 868717c..daf804c 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -1,7 +1,10 @@ import functools import logging +from typing import Any, Generator, Optional, List, Tuple from urllib.parse import quote +from requests import Response + from cloudfoundry_client.errors import InvalidEntity from cloudfoundry_client.json_object import JsonObject from cloudfoundry_client.request_object import Request @@ -10,7 +13,7 @@ class Entity(JsonObject): - def __init__(self, entity_manager, *args, **kwargs): + def __init__(self, entity_manager: 'EntityManager', *args, **kwargs): super(Entity, self).__init__(*args, **kwargs) try: def default_method(m, u): @@ -21,7 +24,7 @@ def default_method(m, u): link_method = link.get('method', 'GET').lower() ref = link['href'] if link_method == 'get': - new_method = functools.partial(entity_manager._paginate, ref) if link_name.endswith('s')\ + new_method = functools.partial(entity_manager._paginate, ref) if link_name.endswith('s') \ else functools.partial(entity_manager._get, ref) elif link_method == 'post': new_method = functools.partial(entity_manager._post, ref) @@ -38,41 +41,41 @@ def default_method(m, u): class EntityManager(object): - def __init__(self, target_endpoint, client, entity_uri): + def __init__(self, target_endpoint: str, client: 'CloudFoundryClient', entity_uri: str): self.target_endpoint = target_endpoint self.entity_uri = entity_uri self.client = client - def _post(self, url, data=None, files=None): + def _post(self, url: str, data: dict = None, files: Any = None) -> Entity: response = self.client.post(url, json=data, files=files) _logger.debug('POST - %s - %s', url, response.text) return self._read_response(response) - def _get(self, url): + def _get(self, url: str) -> Entity: response = self.client.get(url) _logger.debug('GET - %s - %s', url, response.text) return self._read_response(response) - def _put(self, data, url): + def _put(self, url: str, data: dict) -> Entity: response = self.client.put(url, json=data) _logger.debug('PUT - %s - %s', url, response.text) return self._read_response(response) - def _patch(self, data, url): + def _patch(self, url: str, data: dict) -> Entity: response = self.client.patch(url, json=data) _logger.debug('PATCH - %s - %s', url, response.text) return self._read_response(response) - def _delete(self, url): + def _delete(self, url: str): response = self.client.delete(url) _logger.debug('DELETE - %s - %s', url, response.text) - def _list(self, requested_path, **kwargs): + def _list(self, requested_path: str, **kwargs) -> Generator[Entity, None, None]: url_requested = EntityManager._get_url_filtered('%s%s' % (self.target_endpoint, requested_path), **kwargs) for element in self._paginate(url_requested): yield element - def _paginate(self, url_requested): + def _paginate(self, url_requested: str) -> Generator[Entity, None, None]: response = self.client.get(url_requested) while True: _logger.debug('GET - %s - %s', url_requested, response.text) @@ -87,62 +90,62 @@ def _paginate(self, url_requested): url_requested = response_json['pagination']['next']['href'] response = self.client.get(url_requested) - def _create(self, data): + def _create(self, data: dict) -> Entity: url = '%s%s' % (self.target_endpoint, self.entity_uri) return self._post(url, data=data) - def _upload_bits(self, resource_id, filename): + def _upload_bits(self, resource_id: str, filename: str) -> Entity: url = '%s%s/%s/upload' % (self.target_endpoint, self.entity_uri, resource_id) files = {'bits': (filename, open(filename, 'rb'))} return self._post(url, files=files) - def _update(self, resource_id, data): + def _update(self, resource_id: str, data: dict) -> Entity: url = '%s%s/%s' % (self.target_endpoint, self.entity_uri, resource_id) - return self._patch(data, url) + return self._patch(url, data) - def _remove(self, resource_id): + def _remove(self, resource_id: str): url = '%s%s/%s' % (self.target_endpoint, self.entity_uri, resource_id) self._delete(url) - def __iter__(self): + def __iter__(self) -> Generator[Entity, None, None]: return self.list() - def __getitem__(self, entity_guid): + def __getitem__(self, entity_guid) -> Entity: return self.get(entity_guid) - def list(self, **kwargs): + def list(self, **kwargs) -> Generator[Entity, None, None]: return self._list(self.entity_uri, **kwargs) - def get_first(self, **kwargs): + def get_first(self, **kwargs) -> Optional[Entity]: kwargs.setdefault('per_page', 1) for entity in self._list(self.entity_uri, **kwargs): return entity return None - def get(self, entity_id, *extra_paths): + def get(self, entity_id: str, *extra_paths) -> Entity: if len(extra_paths) == 0: requested_path = '%s%s/%s' % (self.target_endpoint, self.entity_uri, entity_id) else: requested_path = '%s%s/%s/%s' % (self.target_endpoint, self.entity_uri, entity_id, '/'.join(extra_paths)) return self._get(requested_path) - def _read_response(self, response): + def _read_response(self, response: Response) -> Entity: result = response.json(object_pairs_hook=JsonObject) return self._entity(result) @staticmethod - def _request(**mandatory_parameters): + def _request(**mandatory_parameters) -> Request: return Request(**mandatory_parameters) - def _entity(self, result): + def _entity(self, result: dict): if 'guid' in result: return Entity(self, **result) else: return result @staticmethod - def _get_url_filtered(url, **kwargs): - def _append_encoded_parameter(parameters, args): + def _get_url_filtered(url: str, **kwargs) -> str: + def _append_encoded_parameter(parameters: List[str], args: Tuple[str, Any]) -> List[str]: parameter_name, parameter_value = args[0], args[1] if isinstance(parameter_value, (list, tuple)): parameters.append('%s=%s' % (parameter_name, quote(','.join(parameter_value)))) diff --git a/requirements.txt b/requirements.txt index 764ad21..626a53c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ protobuf==3.6.1 -oauth2-client==1.1.0 +oauth2-client==1.2.0 websocket-client==0.53.0 PyYAML==5.1 From 9b1e5aa03e22ef05598f1f0f69eb1a8c8596924a Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sat, 23 Nov 2019 16:04:26 +0100 Subject: [PATCH 005/264] Use typing to better declare methods --- main/cloudfoundry_client/doppler/client.py | 7 +- main/cloudfoundry_client/v2/apps.py | 69 ++++++++++--------- main/cloudfoundry_client/v2/buildpacks.py | 6 +- main/cloudfoundry_client/v2/entities.py | 7 +- main/cloudfoundry_client/v2/events.py | 8 ++- main/cloudfoundry_client/v2/jobs.py | 4 +- main/cloudfoundry_client/v2/resources.py | 6 +- main/cloudfoundry_client/v2/routes.py | 10 +-- .../v2/service_bindings.py | 11 +-- .../cloudfoundry_client/v2/service_brokers.py | 12 ++-- .../v2/service_instances.py | 23 ++++--- main/cloudfoundry_client/v2/service_keys.py | 10 +-- .../v2/service_plan_visibilities.py | 10 +-- main/cloudfoundry_client/v2/service_plans.py | 8 +-- main/cloudfoundry_client/v3/apps.py | 4 +- main/cloudfoundry_client/v3/buildpacks.py | 22 +++--- main/cloudfoundry_client/v3/domains.py | 33 ++++----- main/cloudfoundry_client/v3/entities.py | 28 ++++++-- main/cloudfoundry_client/v3/feature_flags.py | 6 +- main/cloudfoundry_client/v3/tasks.py | 12 ++-- test/fake_requests.py | 3 +- test/v3/test_domains.py | 30 +++++--- 22 files changed, 200 insertions(+), 129 deletions(-) diff --git a/main/cloudfoundry_client/doppler/client.py b/main/cloudfoundry_client/doppler/client.py index ec867e6..5f88289 100644 --- a/main/cloudfoundry_client/doppler/client.py +++ b/main/cloudfoundry_client/doppler/client.py @@ -1,5 +1,6 @@ import logging import re +from typing import Generator from urllib.parse import urlparse from oauth2_client.credentials_manager import CredentialManager @@ -11,6 +12,8 @@ _logger = logging.getLogger(__name__) +EnvelopeStream = Generator[Envelope, None, None] + class DopplerClient(object): def __init__(self, doppler_endpoint: str, proxy: dict, verify_ssl: bool, credentials_manager: CredentialManager): @@ -27,7 +30,7 @@ def __init__(self, doppler_endpoint: str, proxy: dict, verify_ssl: bool, credent self.proxy_host = proxy_domain[:idx] self.proxy_port = int(proxy_domain[idx + 1:]) - def recent_logs(self, app_guid: str): + def recent_logs(self, app_guid: str) -> EnvelopeStream: url = '%s/apps/%s/recentlogs' % (self.http_doppler_endpoint, app_guid) response = self.credentials_manager.get(url, stream=True) boundary = DopplerClient._extract_boundary(response) @@ -35,7 +38,7 @@ def recent_logs(self, app_guid: str): for part in DopplerClient._read_multi_part_response(response, boundary): yield DopplerClient._parse_envelope(part) - def stream_logs(self, app_guid: str): + def stream_logs(self, app_guid: str) -> EnvelopeStream: url = '%s/apps/%s/stream' % (self.ws_doppler_endpoint, app_guid) with WebsocketFrameReader(url, lambda: self.credentials_manager._access_token, diff --git a/main/cloudfoundry_client/v2/apps.py b/main/cloudfoundry_client/v2/apps.py index 6564229..4e237c1 100644 --- a/main/cloudfoundry_client/v2/apps.py +++ b/main/cloudfoundry_client/v2/apps.py @@ -3,40 +3,42 @@ import os from http import HTTPStatus from time import sleep +from typing import Optional, Dict +from cloudfoundry_client.doppler.client import EnvelopeStream from cloudfoundry_client.errors import InvalidStatusCode from cloudfoundry_client.json_object import JsonObject -from cloudfoundry_client.v2.entities import Entity, EntityManager +from cloudfoundry_client.v2.entities import Entity, EntityManager, PaginateEntities _logger = logging.getLogger(__name__) -class _Application(Entity): - def instances(self): +class Application(Entity): + def instances(self) -> Dict[str, dict]: return self.client.v2.apps.get_instances(self['metadata']['guid']) - def start(self): + def start(self) -> 'Application': return self.client.v2.apps.start(self['metadata']['guid']) - def stop(self): + def stop(self) -> 'Application': return self.client.v2.apps.stop(self['metadata']['guid']) - def stats(self): + def stats(self) -> Dict[str, JsonObject]: return self.client.v2.apps.get_stats(self['metadata']['guid']) - def env(self): + def env(self) -> Dict[str, JsonObject]: return self.client.v2.apps.get_env(self['metadata']['guid']) - def summary(self): + def summary(self) -> JsonObject: return self.client.v2.apps.get_summary(self['metadata']['guid']) - def restage(self): + def restage(self) -> 'Application': return self.client.v2.apps.restage(self['metadata']['guid']) - def recent_logs(self): + def recent_logs(self) -> EnvelopeStream: return self.client.doppler.recent_logs(self['metadata']['guid']) - def stream_logs(self): + def stream_logs(self) -> EnvelopeStream: return self.client.doppler.stream_logs(self['metadata']['guid']) @@ -46,36 +48,37 @@ class AppManager(EntityManager): 'diego', 'enable_ssh', 'docker_image', 'docker_credentials', 'environment_json', 'production', 'console', 'debug', 'staging_failed_reason', 'staging_failed_description', 'ports'] - def __init__(self, target_endpoint, client): + def __init__(self, target_endpoint: str, client: 'CloudFoundryClient'): super(AppManager, self).__init__(target_endpoint, client, '/v2/apps', - lambda pairs: _Application(target_endpoint, client, pairs)) + lambda pairs: Application(target_endpoint, client, pairs)) - def get_stats(self, application_guid): + def get_stats(self, application_guid: str) -> Dict[str, JsonObject]: return self._get('%s/%s/stats' % (self.entity_uri, application_guid), JsonObject) - def get_instances(self, application_guid): + def get_instances(self, application_guid: str) -> Dict[str, JsonObject]: return self._get('%s/%s/instances' % (self.entity_uri, application_guid), JsonObject) - def get_env(self, application_guid): + def get_env(self, application_guid: str) -> Dict[str, JsonObject]: return self._get('%s/%s/env' % (self.entity_uri, application_guid), JsonObject) - def get_summary(self, application_guid): + def get_summary(self, application_guid: str) -> JsonObject: return self._get('%s/%s/summary' % (self.entity_uri, application_guid), JsonObject) - def associate_route(self, application_guid, route_guid): + def associate_route(self, application_guid: str, route_guid: str) -> Application: self._put('%s%s/%s/routes/%s' % (self.target_endpoint, self.entity_uri, application_guid, route_guid)) - def list_routes(self, application_guid, **kwargs): + def list_routes(self, application_guid: str, **kwargs) -> PaginateEntities: return self.client.v2.routes._list('%s/%s/routes' % (self.entity_uri, application_guid), **kwargs) - def remove_route(self, application_guid, route_guid): + def remove_route(self, application_guid: str, route_guid: str): self._delete('%s%s/%s/routes/%s' % (self.target_endpoint, self.entity_uri, application_guid, route_guid)) - def list_service_bindings(self, application_guid, **kwargs): + def list_service_bindings(self, application_guid: str, **kwargs) -> PaginateEntities: return self.client.v2.service_bindings._list('%s/%s/service_bindings' % (self.entity_uri, application_guid), **kwargs) - def start(self, application_guid, check_time=0.5, timeout=300, asynchronous=False): + def start(self, application_guid: str, check_time: Optional[float] = 0.5, timeout: Optional[float] = 300.0, + asynchronous: Optional[bool] = False) -> Application: result = super(AppManager, self)._update(application_guid, dict(state='STARTED')) if asynchronous: @@ -85,7 +88,8 @@ def start(self, application_guid, check_time=0.5, timeout=300, asynchronous=Fals self._wait_for_instances_in_state(application_guid, summary['instances'], 'RUNNING', check_time, timeout) return result - def stop(self, application_guid, check_time=0.5, timeout=500, asynchronous=False): + def stop(self, application_guid: str, check_time: Optional[float] = 0.5, timeout: Optional[float] = 500.0, + asynchronous: Optional[bool] = False) -> Application: result = super(AppManager, self)._update(application_guid, dict(state='STOPPED')) if asynchronous: return result @@ -93,23 +97,23 @@ def stop(self, application_guid, check_time=0.5, timeout=500, asynchronous=False self._wait_for_instances_in_state(application_guid, 0, 'STOPPED', check_time, timeout) return result - def restage(self, application_guid): + def restage(self, application_guid: str) -> Application: return self._post("%s%s/%s/restage" % (self.target_endpoint, self.entity_uri, application_guid)) - def create(self, **kwargs): + def create(self, **kwargs) -> Application: if kwargs.get('name') is None or kwargs.get('space_guid') is None: raise AssertionError('Please provide a name and a space_guid') request = AppManager._generate_application_update_request(**kwargs) return super(AppManager, self)._create(request) - def update(self, application_guid, **kwargs): + def update(self, application_guid: str, **kwargs) -> Application: request = AppManager._generate_application_update_request(**kwargs) return super(AppManager, self)._update(application_guid, request) - def remove(self, application_guid): + def remove(self, application_guid: str): super(AppManager, self)._remove(application_guid) - def upload(self, application_guid, resources, application, asynchronous=False): + def upload(self, application_guid: str, resources, application: str, asynchronous: Optional[bool] = False): application_size = os.path.getsize(application) with open(application, 'rb') as binary_file: return self.client.put("%s%s/%s/bits" % (self.target_endpoint, self.entity_uri, application_guid), @@ -123,10 +127,11 @@ def upload(self, application_guid, resources, application, asynchronous=False): .json(object_pairs_hook=JsonObject) @staticmethod - def _generate_application_update_request(**kwargs): + def _generate_application_update_request(**kwargs) -> dict: return {key: kwargs[key] for key in AppManager.APPLICATION_FIELDS if key in kwargs} - def _wait_for_instances_in_state(self, application_guid, number_required, state_expected, check_time, timeout): + def _wait_for_instances_in_state(self, application_guid: str, number_required: int, state_expected: str, + check_time: float, timeout: float): all_in_expected_state = False sum_waiting = 0 while not all_in_expected_state: @@ -146,7 +151,7 @@ def _wait_for_instances_in_state(self, application_guid, number_required, state_ sleep(check_time) sum_waiting += check_time - def _safe_get_instances(self, application_guid): + def _safe_get_instances(self, application_guid: str) -> JsonObject: try: return self.get_instances(application_guid) except InvalidStatusCode as ex: @@ -155,7 +160,7 @@ def _safe_get_instances(self, application_guid): # 170002: staging not finished # 220001: instances error if code == 220001 or code == 170002: - return {} + return JsonObject() else: _logger.error("") raise diff --git a/main/cloudfoundry_client/v2/buildpacks.py b/main/cloudfoundry_client/v2/buildpacks.py index 005896a..800572e 100644 --- a/main/cloudfoundry_client/v2/buildpacks.py +++ b/main/cloudfoundry_client/v2/buildpacks.py @@ -1,9 +1,9 @@ -from cloudfoundry_client.v2.entities import EntityManager +from cloudfoundry_client.v2.entities import EntityManager, Entity class BuildpackManager(EntityManager): - def __init__(self, target_endpoint, client): + def __init__(self, target_endpoint: str, client: 'CloudFoundryClient'): super(BuildpackManager, self).__init__(target_endpoint, client, '/v2/buildpacks') - def update(self, buildpack_guid, parameters): + def update(self, buildpack_guid: str, parameters: dict) -> Entity: return super(BuildpackManager, self)._update(buildpack_guid, parameters) diff --git a/main/cloudfoundry_client/v2/entities.py b/main/cloudfoundry_client/v2/entities.py index a8ae861..3db4480 100644 --- a/main/cloudfoundry_client/v2/entities.py +++ b/main/cloudfoundry_client/v2/entities.py @@ -46,6 +46,7 @@ def __init__(self, target_endpoint: str, client: 'CloudFoundryClient', *args, ** EntityBuilder = Callable[[List[Tuple[str, Any]]], Entity] +PaginateEntities = Generator[Entity, None, None] class EntityManager(object): list_query_parameters = ['page', 'results-per-page', 'order-direction'] @@ -67,7 +68,7 @@ def _get(self, requested_path: str, entity_builder: Optional[EntityBuilder] = No return self._read_response(response, entity_builder) def _list(self, requested_path: str, entity_builder: Optional[EntityBuilder] = None, **kwargs) \ - -> Generator[Entity, None, None]: + -> PaginateEntities: url_requested = self._get_url_filtered('%s%s' % (self.target_endpoint, requested_path), **kwargs) response = self.client.get(url_requested) entity_builder = self._get_entity_builder(entity_builder) @@ -108,13 +109,13 @@ def _delete(self, url: str, **kwargs): response = self.client.delete(url, **kwargs) _logger.debug('DELETE - %s - %s', url, response.text) - def __iter__(self) -> Generator[Entity, None, None]: + def __iter__(self) -> PaginateEntities: return self.list() def __getitem__(self, entity_guid) -> Entity: return self.get(entity_guid) - def list(self, **kwargs) -> Generator[Entity, None, None]: + def list(self, **kwargs) -> PaginateEntities: return self._list(self.entity_uri, **kwargs) def get_first(self, **kwargs) -> Optional[Entity]: diff --git a/main/cloudfoundry_client/v2/events.py b/main/cloudfoundry_client/v2/events.py index d47c6ee..b7b898e 100644 --- a/main/cloudfoundry_client/v2/events.py +++ b/main/cloudfoundry_client/v2/events.py @@ -1,9 +1,11 @@ -from cloudfoundry_client.v2.entities import EntityManager +from typing import Generator + +from cloudfoundry_client.v2.entities import EntityManager, Entity class EventManager(EntityManager): - def __init__(self, target_endpoint, client): + def __init__(self, target_endpoint: str, client: 'CloudFoundryClient'): super(EventManager, self).__init__(target_endpoint, client, '/v2/events') - def list_by_type(self, event_type): + def list_by_type(self, event_type: str) -> Generator[Entity, None, None]: return self._list(self.entity_uri, type=event_type) diff --git a/main/cloudfoundry_client/v2/jobs.py b/main/cloudfoundry_client/v2/jobs.py index f895270..17f86fc 100644 --- a/main/cloudfoundry_client/v2/jobs.py +++ b/main/cloudfoundry_client/v2/jobs.py @@ -2,9 +2,9 @@ class JobManager(object): - def __init__(self, target_endpoint, client): + def __init__(self, target_endpoint: str, client: 'CloudFoundryClient'): self.target_endpoint = target_endpoint self.client = client - def get(self, job_guid): + def get(self, job_guid: str) -> JsonObject: return self.client.get('%s/v2/jobs/%s' % (self.target_endpoint, job_guid)).json(object_pairs_hook=JsonObject) diff --git a/main/cloudfoundry_client/v2/resources.py b/main/cloudfoundry_client/v2/resources.py index 83c8c37..e7fe88a 100644 --- a/main/cloudfoundry_client/v2/resources.py +++ b/main/cloudfoundry_client/v2/resources.py @@ -1,11 +1,13 @@ +from typing import List + from cloudfoundry_client.json_object import JsonObject class ResourceManager(object): - def __init__(self, target_endpoint, client): + def __init__(self, target_endpoint: str, client: 'CloudFoundryClient'): self.target_endpoint = target_endpoint self.client = client - def match(self, items): + def match(self, items: List[dict]) -> List[JsonObject]: response = self.client.put('%s/v2/resource_match' % self.client.info.api_endpoint, json=items) return response.json(object_pairs_hook=JsonObject) diff --git a/main/cloudfoundry_client/v2/routes.py b/main/cloudfoundry_client/v2/routes.py index 7a3f506..8a2702e 100644 --- a/main/cloudfoundry_client/v2/routes.py +++ b/main/cloudfoundry_client/v2/routes.py @@ -1,11 +1,13 @@ -from cloudfoundry_client.v2.entities import EntityManager +from typing import Optional + +from cloudfoundry_client.v2.entities import EntityManager, Entity class RouteManager(EntityManager): - def __init__(self, target_endpoint, client): + def __init__(self, target_endpoint: str, client: 'CloudFoundryClient'): super(RouteManager, self).__init__(target_endpoint, client, '/v2/routes') - def create_tcp_route(self, domain_guid, space_guid, port=None): + def create_tcp_route(self, domain_guid: str, space_guid: str, port: Optional[int] = None) -> Entity: request = self._request(domain_guid=domain_guid, space_guid=space_guid) if port is None: return super(RouteManager, self)._create(request, params=dict(generate_port=True)) @@ -13,6 +15,6 @@ def create_tcp_route(self, domain_guid, space_guid, port=None): request['port'] = port return super(RouteManager, self)._create(request) - def create_host_route(self, domain_guid, space_guid, host, path=''): + def create_host_route(self, domain_guid: str, space_guid: str, host: str, path: Optional[str] = '') -> Entity: request = dict(domain_guid=domain_guid, space_guid=space_guid, host=host, path=path) return super(RouteManager, self)._create(request) diff --git a/main/cloudfoundry_client/v2/service_bindings.py b/main/cloudfoundry_client/v2/service_bindings.py index 30c79ba..3d34865 100644 --- a/main/cloudfoundry_client/v2/service_bindings.py +++ b/main/cloudfoundry_client/v2/service_bindings.py @@ -1,15 +1,18 @@ -from cloudfoundry_client.v2.entities import EntityManager +from typing import Optional + +from cloudfoundry_client.v2.entities import EntityManager, Entity class ServiceBindingManager(EntityManager): - def __init__(self, target_endpoint, client): + def __init__(self, target_endpoint: str, client: 'CloudFoundryClient'): super(ServiceBindingManager, self).__init__(target_endpoint, client, '/v2/service_bindings') - def create(self, app_guid, instance_guid, parameters=None, name=None): + def create(self, app_guid: str, instance_guid: str, parameters: Optional[dict] = None, + name: Optional[str] = None) -> Entity: request = self._request(app_guid=app_guid, service_instance_guid=instance_guid) request['parameters'] = parameters request['name'] = name return super(ServiceBindingManager, self)._create(request) - def remove(self, binding_id): + def remove(self, binding_id: str): super(ServiceBindingManager, self)._remove(binding_id) diff --git a/main/cloudfoundry_client/v2/service_brokers.py b/main/cloudfoundry_client/v2/service_brokers.py index 0f69c85..b57b798 100644 --- a/main/cloudfoundry_client/v2/service_brokers.py +++ b/main/cloudfoundry_client/v2/service_brokers.py @@ -1,17 +1,21 @@ -from cloudfoundry_client.v2.entities import EntityManager +from typing import Optional + +from cloudfoundry_client.v2.entities import EntityManager, Entity class ServiceBrokerManager(EntityManager): - def __init__(self, target_endpoint, client): + def __init__(self, target_endpoint: str, client: 'CloudFoundryClient'): super(ServiceBrokerManager, self).__init__(target_endpoint, client, '/v2/service_brokers') - def create(self, broker_url, broker_name, auth_username, auth_password, space_guid=None): + def create(self, broker_url: str, broker_name: str, auth_username: str, auth_password: str, + space_guid: Optional[str] = None) -> Entity: request = self._request(broker_url=broker_url, name=broker_name, auth_username=auth_username, auth_password=auth_password) request['space_guid'] = space_guid return super(ServiceBrokerManager, self)._create(request) - def update(self, broker_guid, broker_url=None, broker_name=None, auth_username=None, auth_password=None): + def update(self, broker_guid: str, broker_url: Optional[str] = None, broker_name: Optional[str] = None, + auth_username: Optional[str] = None, auth_password: Optional[str] = None) -> Entity: request = self._request() request['broker_url'] = broker_url request['name'] = broker_name diff --git a/main/cloudfoundry_client/v2/service_instances.py b/main/cloudfoundry_client/v2/service_instances.py index aa91284..6e67f0a 100644 --- a/main/cloudfoundry_client/v2/service_instances.py +++ b/main/cloudfoundry_client/v2/service_instances.py @@ -1,21 +1,26 @@ -from cloudfoundry_client.v2.entities import EntityManager -from cloudfoundry_client.json_object import JsonObject +from typing import Optional, List, Dict + +from cloudfoundry_client.v2.entities import EntityManager, Entity class ServiceInstanceManager(EntityManager): list_query_parameters = ['page', 'results-per-page', 'order-direction', 'return_user_provided_service_instances'] - def __init__(self, target_endpoint, client): + def __init__(self, target_endpoint: str, client: 'CloudFoundryClient'): super(ServiceInstanceManager, self).__init__(target_endpoint, client, '/v2/service_instances') - def create(self, space_guid, instance_name, plan_guid, parameters=None, tags=None, accepts_incomplete=False): + def create(self, space_guid: str, instance_name: str, plan_guid: str, + parameters: Optional[dict] = None, tags: List[str] = None, + accepts_incomplete: Optional[bool] = False) -> Entity: request = self._request(name=instance_name, space_guid=space_guid, service_plan_guid=plan_guid) request['parameters'] = parameters request['tags'] = tags params = None if not accepts_incomplete else dict(accepts_incomplete="true") return super(ServiceInstanceManager, self)._create(request, params=params) - def update(self, instance_guid, instance_name=None, plan_guid=None, parameters=None, tags=None, accepts_incomplete=False): + def update(self, instance_guid: str, instance_name: Optional[str] = None, plan_guid: Optional[str] = None, + parameters: Optional[dict] = None, tags: List[str] = None, + accepts_incomplete: Optional[bool] = False) -> Entity: request = self._request() request['name'] = instance_name request['service_plan_guid'] = plan_guid @@ -24,14 +29,14 @@ def update(self, instance_guid, instance_name=None, plan_guid=None, parameters=N params = None if not accepts_incomplete else dict(accepts_incomplete="true") return super(ServiceInstanceManager, self)._update(instance_guid, request, params=params) - def list_permissions(self, instance_guid): + def list_permissions(self, instance_guid: str) -> Dict[str, bool]: return super(ServiceInstanceManager, self)._get('%s/%s/permissions' % (self.entity_uri, instance_guid), - JsonObject) + dict) - def remove(self, instance_guid, accepts_incomplete=False, purge=False): + def remove(self, instance_guid: str, accepts_incomplete: Optional[bool] = False, purge: Optional[bool] = False): parameters = {} if accepts_incomplete: parameters['accepts_incomplete'] = "true" if purge: - parameters['purge']= "true" + parameters['purge'] = "true" super(ServiceInstanceManager, self)._remove(instance_guid, params=parameters) diff --git a/main/cloudfoundry_client/v2/service_keys.py b/main/cloudfoundry_client/v2/service_keys.py index 58ff7a8..287268c 100644 --- a/main/cloudfoundry_client/v2/service_keys.py +++ b/main/cloudfoundry_client/v2/service_keys.py @@ -1,14 +1,16 @@ -from cloudfoundry_client.v2.entities import EntityManager +from typing import Optional + +from cloudfoundry_client.v2.entities import EntityManager, Entity class ServiceKeyManager(EntityManager): - def __init__(self, target_endpoint, client): + def __init__(self, target_endpoint: str, client: 'CloudfoundryClient'): super(ServiceKeyManager, self).__init__(target_endpoint, client, '/v2/service_keys') - def create(self, service_instance_guid, name, parameters=None): + def create(self, service_instance_guid: str, name: str, parameters: Optional[dict] = None) -> Entity: request = self._request(service_instance_guid=service_instance_guid, name=name) request['parameters'] = parameters return super(ServiceKeyManager, self)._create(request) - def remove(self, key_guid): + def remove(self, key_guid: str): super(ServiceKeyManager, self)._remove(key_guid) diff --git a/main/cloudfoundry_client/v2/service_plan_visibilities.py b/main/cloudfoundry_client/v2/service_plan_visibilities.py index a1f449d..15bb285 100644 --- a/main/cloudfoundry_client/v2/service_plan_visibilities.py +++ b/main/cloudfoundry_client/v2/service_plan_visibilities.py @@ -1,21 +1,21 @@ -from cloudfoundry_client.v2.entities import EntityManager +from cloudfoundry_client.v2.entities import EntityManager, Entity class ServicePlanVisibilityManager(EntityManager): - def __init__(self, target_endpoint, client): + def __init__(self, target_endpoint: str, client: 'CloudFoundryClient'): super(ServicePlanVisibilityManager, self).__init__(target_endpoint, client, '/v2/service_plan_visibilities') - def create(self, service_plan_guid, organization_guid): + def create(self, service_plan_guid: str, organization_guid: str) -> Entity: request = self._request() request['service_plan_guid'] = service_plan_guid request['organization_guid'] = organization_guid return super(ServicePlanVisibilityManager, self)._create(request) - def update(self, spv_guid, service_plan_guid, organization_guid): + def update(self, spv_guid: str, service_plan_guid: str, organization_guid: str) -> Entity: request = self._request() request['service_plan_guid'] = service_plan_guid request['organization_guid'] = organization_guid return super(ServicePlanVisibilityManager, self)._update(spv_guid, request) - def remove(self, spv_guid): + def remove(self, spv_guid: str): super(ServicePlanVisibilityManager, self)._remove(spv_guid) diff --git a/main/cloudfoundry_client/v2/service_plans.py b/main/cloudfoundry_client/v2/service_plans.py index 8a6a2a1..50c0a15 100644 --- a/main/cloudfoundry_client/v2/service_plans.py +++ b/main/cloudfoundry_client/v2/service_plans.py @@ -1,13 +1,13 @@ -from cloudfoundry_client.v2.entities import EntityManager +from cloudfoundry_client.v2.entities import EntityManager, Entity, PaginateEntities class ServicePlanManager(EntityManager): - def __init__(self, target_endpoint, client): + def __init__(self, target_endpoint: str, client: 'CloudFoundryClient'): super(ServicePlanManager, self).__init__(target_endpoint, client, '/v2/service_plans') - def create_from_resource_file(self, path): + def create_from_resource_file(self, path: str) -> Entity: raise NotImplementedError('No creation allowed') - def list_instances(self, service_plan_guid, **kwargs): + def list_instances(self, service_plan_guid: str, **kwargs) -> PaginateEntities: return self.client.v2.service_instances._list('%s/%s/service_instances' % (self.entity_uri, service_plan_guid), **kwargs) diff --git a/main/cloudfoundry_client/v3/apps.py b/main/cloudfoundry_client/v3/apps.py index 5cc4146..718cfed 100644 --- a/main/cloudfoundry_client/v3/apps.py +++ b/main/cloudfoundry_client/v3/apps.py @@ -2,8 +2,8 @@ class AppManager(EntityManager): - def __init__(self, target_endpoint, client): + def __init__(self, target_endpoint: str, client: 'CloudfoundryClient'): super(AppManager, self).__init__(target_endpoint, client, '/v3/apps') - def remove(self, application_guid): + def remove(self, application_guid: str): super(AppManager, self)._remove(application_guid) diff --git a/main/cloudfoundry_client/v3/buildpacks.py b/main/cloudfoundry_client/v3/buildpacks.py index 1c78882..351d166 100644 --- a/main/cloudfoundry_client/v3/buildpacks.py +++ b/main/cloudfoundry_client/v3/buildpacks.py @@ -1,12 +1,16 @@ -from cloudfoundry_client.v3.entities import EntityManager +from typing import Optional + +from cloudfoundry_client.v3.entities import EntityManager, Entity class BuildpackManager(EntityManager): - def __init__(self, target_endpoint, client): + def __init__(self, target_endpoint: str, client: 'CloudfoundryClient'): super(BuildpackManager, self).__init__(target_endpoint, client, '/v3/buildpacks') - def create(self, name, position=0, enabled=True, locked=False, stack=None, - meta_labels=None, meta_annotations=None): + def create(self, name: str, position: Optional[int] = 0, + enabled: Optional[bool] = True, locked: Optional[bool] = False, + stack: Optional[str] = None, + meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Entity: data = { 'name': name, 'position': position, @@ -20,11 +24,13 @@ def create(self, name, position=0, enabled=True, locked=False, stack=None, } return super(BuildpackManager, self)._create(data) - def remove(self, buildpack_guid): + def remove(self, buildpack_guid: str): super(BuildpackManager, self)._remove(buildpack_guid) - def update(self, buildpack_guid, name, position=0, enabled=True, - locked=False, stack=None, meta_labels=None, meta_annotations=None): + def update(self, buildpack_guid: str, name: str, position: Optional[int] = 0, + enabled: Optional[bool] = True, locked: Optional[bool] = False, + stack: Optional[str] = None, + meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Entity: data = { 'name': name, 'position': position, @@ -38,5 +44,5 @@ def update(self, buildpack_guid, name, position=0, enabled=True, } return super(BuildpackManager, self)._update(buildpack_guid, data) - def upload(self, buildpack_guid, buildpack_zip): + def upload(self, buildpack_guid: str, buildpack_zip: str) -> Entity: return super(BuildpackManager, self)._upload_bits(buildpack_guid, buildpack_zip) diff --git a/main/cloudfoundry_client/v3/domains.py b/main/cloudfoundry_client/v3/domains.py index af91a99..9427ca6 100644 --- a/main/cloudfoundry_client/v3/domains.py +++ b/main/cloudfoundry_client/v3/domains.py @@ -1,12 +1,17 @@ -from cloudfoundry_client.v3.entities import EntityManager +from typing import Optional + +from cloudfoundry_client.v3.entities import EntityManager, ToOneRelationship, ToManyRelationship, PaginateEntities, \ + Entity class DomainManager(EntityManager): - def __init__(self, target_endpoint, client): + def __init__(self, target_endpoint: str, client: 'CloudfoundryClient'): super(DomainManager, self).__init__(target_endpoint, client, '/v3/domains') - def create(self, name, internal=False, organization=None, shared_organizations=None, - meta_labels=None, meta_annotations=None): + def create(self, name: str, internal: Optional[bool] = False, + organization: Optional[ToOneRelationship] = None, + shared_organizations: Optional[ToManyRelationship] = None, + meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None): data = { 'name': name, 'internal': internal, @@ -19,11 +24,12 @@ def create(self, name, internal=False, organization=None, shared_organizations=N } return super(DomainManager, self)._create(data) - def list_domains_for_org(self, org_guid, **kwargs): + def list_domains_for_org(self, org_guid: str, **kwargs) -> PaginateEntities: uri = '/v3/organizations/{guid}/domains'.format(guid=org_guid) return self._list(uri, **kwargs) - def update(self, domain_guid, meta_labels=None, meta_annotations=None): + def update(self, domain_guid: str, + meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Entity: data = { 'metadata': { 'labels': meta_labels, @@ -32,25 +38,20 @@ def update(self, domain_guid, meta_labels=None, meta_annotations=None): } return super(DomainManager, self)._update(domain_guid, data) - def remove(self, domain_guid): + def remove(self, domain_guid: str): super(DomainManager, self)._remove(domain_guid) - def __create_shared_domain_url(self, domain_guid): + def __create_shared_domain_url(self, domain_guid: str) -> str: # TODO use url parser for this return '{endpoint}{entity}/{domain}/relationships/shared_organizations' \ ''.format(endpoint=self.target_endpoint, entity=self.entity_uri, domain=domain_guid) - def share_domain(self, domain_guid, organization_guids): - if type(organization_guids) is not list(): - organization_guids = [organization_guids] + def share_domain(self, domain_guid: str, organization_guids: ToManyRelationship): url = self.__create_shared_domain_url(domain_guid) - data = [] - for org in organization_guids: - data.append({'guid': org}) - return super(DomainManager, self)._post(url, data=data) + return super(DomainManager, self)._post(url, data=organization_guids) - def unshare_domain(self, domain_guid, org_guid): + def unshare_domain(self, domain_guid: str, org_guid: str): url = '{uri}/{org}'.format(uri=self.__create_shared_domain_url(domain_guid), org=org_guid) super(DomainManager, self)._delete(url) diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index daf804c..f93cf3b 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -40,13 +40,31 @@ def default_method(m, u): raise InvalidEntity(**self) +PaginateEntities = Generator[Entity, None, None] + + +class Relationship(JsonObject): + def __init__(self, guid: str): + super(Relationship, self).__init__(guid=guid) + + +class ToOneRelationship(JsonObject): + def __init__(self, guid: str): + super(ToOneRelationship, self).__init__(data=Relationship(guid)) + + +class ToManyRelationship(JsonObject): + def __init__(self, *guids: str): + super(ToManyRelationship, self).__init__(data=[Relationship(guid) for guid in guids]) + + class EntityManager(object): def __init__(self, target_endpoint: str, client: 'CloudFoundryClient', entity_uri: str): self.target_endpoint = target_endpoint self.entity_uri = entity_uri self.client = client - def _post(self, url: str, data: dict = None, files: Any = None) -> Entity: + def _post(self, url: str, data: Optional[dict] = None, files: Any = None) -> Entity: response = self.client.post(url, json=data, files=files) _logger.debug('POST - %s - %s', url, response.text) return self._read_response(response) @@ -70,12 +88,12 @@ def _delete(self, url: str): response = self.client.delete(url) _logger.debug('DELETE - %s - %s', url, response.text) - def _list(self, requested_path: str, **kwargs) -> Generator[Entity, None, None]: + def _list(self, requested_path: str, **kwargs) -> PaginateEntities: url_requested = EntityManager._get_url_filtered('%s%s' % (self.target_endpoint, requested_path), **kwargs) for element in self._paginate(url_requested): yield element - def _paginate(self, url_requested: str) -> Generator[Entity, None, None]: + def _paginate(self, url_requested: str) -> PaginateEntities: response = self.client.get(url_requested) while True: _logger.debug('GET - %s - %s', url_requested, response.text) @@ -107,13 +125,13 @@ def _remove(self, resource_id: str): url = '%s%s/%s' % (self.target_endpoint, self.entity_uri, resource_id) self._delete(url) - def __iter__(self) -> Generator[Entity, None, None]: + def __iter__(self) -> PaginateEntities: return self.list() def __getitem__(self, entity_guid) -> Entity: return self.get(entity_guid) - def list(self, **kwargs) -> Generator[Entity, None, None]: + def list(self, **kwargs) -> PaginateEntities: return self._list(self.entity_uri, **kwargs) def get_first(self, **kwargs) -> Optional[Entity]: diff --git a/main/cloudfoundry_client/v3/feature_flags.py b/main/cloudfoundry_client/v3/feature_flags.py index 2b94163..099d8ae 100644 --- a/main/cloudfoundry_client/v3/feature_flags.py +++ b/main/cloudfoundry_client/v3/feature_flags.py @@ -1,11 +1,13 @@ +from typing import Optional + from cloudfoundry_client.v3.entities import EntityManager class FeatureFlagManager(EntityManager): - def __init__(self, target_endpoint, client): + def __init__(self, target_endpoint: str, client: 'CloudfoundryClient'): super(FeatureFlagManager, self).__init__(target_endpoint, client, '/v3/feature_flags') - def update(self, name, enabled=True, custom_error_message=None): + def update(self, name: str, enabled: Optional[bool] = True, custom_error_message: Optional[str] = None): data = { 'enabled': enabled, 'custom_error_message': custom_error_message diff --git a/main/cloudfoundry_client/v3/tasks.py b/main/cloudfoundry_client/v3/tasks.py index 9e8d07b..a92e42c 100644 --- a/main/cloudfoundry_client/v3/tasks.py +++ b/main/cloudfoundry_client/v3/tasks.py @@ -1,11 +1,15 @@ -from cloudfoundry_client.v3.entities import EntityManager +from typing import Optional + +from cloudfoundry_client.v3.entities import EntityManager, Entity class TaskManager(EntityManager): - def __init__(self, target_endpoint, client): + def __init__(self, target_endpoint: str, client: 'CloudfoundryClient'): super(TaskManager, self).__init__(target_endpoint, client, '/v3/tasks') - def create(self, application_guid, command, name=None, disk_in_mb=None, memory_in_mb=None, droplet_guid=None): + def create(self, application_guid: str, command: str, + name: Optional[str] = None, disk_in_mb: Optional[int] = None, memory_in_mb: Optional[int] = None, + droplet_guid: Optional[str] = None) -> Entity: request = self._request(command=command) request['name'] = name request['disk_in_mb'] = disk_in_mb @@ -13,5 +17,5 @@ def create(self, application_guid, command, name=None, disk_in_mb=None, memory_i request['droplet_guid'] = droplet_guid return self._post('%s/v3/apps/%s/tasks' % (self.target_endpoint, application_guid), data=request) - def cancel(self, task_guid): + def cancel(self, task_guid: str) -> Entity: return self._post('%s/v3/tasks/%s/actions/cancel' % (self.target_endpoint, task_guid)) diff --git a/test/fake_requests.py b/test/fake_requests.py index 9cddcc2..0849b20 100644 --- a/test/fake_requests.py +++ b/test/fake_requests.py @@ -1,6 +1,7 @@ import os from http import HTTPStatus from json import loads +from typing import Optional from unittest.mock import MagicMock @@ -44,7 +45,7 @@ def get_fixtures_path(*paths): return os.path.join(os.path.dirname(__file__), 'fixtures', *paths) -def mock_response(uri: str, status_code: HTTPStatus, headers: dict, *path_parts: str): +def mock_response(uri: str, status_code: HTTPStatus, headers: Optional[dict], *path_parts: str): global TARGET_ENDPOINT if len(path_parts) > 0: file_name = path_parts[len(path_parts) - 1] diff --git a/test/v3/test_domains.py b/test/v3/test_domains.py index 192d6a1..bc32e92 100644 --- a/test/v3/test_domains.py +++ b/test/v3/test_domains.py @@ -5,7 +5,7 @@ import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase -from cloudfoundry_client.v3.entities import Entity +from cloudfoundry_client.v3.entities import Entity, ToManyRelationship, ToOneRelationship from fake_requests import mock_response @@ -47,9 +47,9 @@ def test_update(self): result = self.client.v3.domains.update('domain_id') self.client.patch.assert_called_with(self.client.patch.return_value.url, json={'metadata': { - 'labels': None, - 'annotations': None - } + 'labels': None, + 'annotations': None + } }) self.assertIsNotNone(result) @@ -61,7 +61,7 @@ def test_create(self): 'v3', 'domains', 'POST_response.json') result = self.client.v3.domains.create('domain_id', internal=False, - organization=None, + organization=ToOneRelationship('organization-guid'), shared_organizations=None, meta_labels=None, meta_annotations=None) @@ -69,13 +69,17 @@ def test_create(self): files=None, json={'name': 'domain_id', 'internal': False, - 'organization': None, + 'organization': { + 'data': { + 'guid': 'organization-guid' + } + }, 'shared_organizations': None, 'metadata': { 'labels': None, 'annotations': None } - }) + }) self.assertIsNotNone(result) def test_remove(self): @@ -103,10 +107,16 @@ def test_share_domain(self): HTTPStatus.CREATED, None, 'v3', 'domains', 'POST_{id}_relationships_shared_organizations_response.json') - result = self.client.v3.domains.share_domain('domain_id', 'org_id') + result = self.client.v3.domains.share_domain('domain_id', + ToManyRelationship('organization-guid-1', 'organization-guid-2')) self.client.post.assert_called_with(self.client.post.return_value.url, files=None, - json=[{'guid': 'org_id'}]) + json={ + 'data': [ + {'guid': 'organization-guid-1'}, + {'guid': 'organization-guid-2'} + ] + }) self.assertIsNotNone(result) def test_unshare_domain(self): @@ -131,7 +141,7 @@ def test_main_list_domains(self): @patch.object(sys, 'argv', ['main', 'get_domain', '3a5d3d89-3f89-4f05-8188-8a2b298c79d5']) def test_main_get_domain(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): + new=lambda: self.client): self.client.get.return_value = mock_response('/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5', HTTPStatus.OK, None, From 3df5feffacb658eaa2ed65770ac266ea9611d98a Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sun, 24 Nov 2019 16:45:41 +0100 Subject: [PATCH 006/264] Refactor V3 entities to support different types --- main/cloudfoundry_client/v3/domains.py | 15 +++- main/cloudfoundry_client/v3/entities.py | 83 +++++++++++++------- main/cloudfoundry_client/v3/feature_flags.py | 4 +- test/v3/test_domains.py | 12 ++- 4 files changed, 81 insertions(+), 33 deletions(-) diff --git a/main/cloudfoundry_client/v3/domains.py b/main/cloudfoundry_client/v3/domains.py index 9427ca6..60e8da1 100644 --- a/main/cloudfoundry_client/v3/domains.py +++ b/main/cloudfoundry_client/v3/domains.py @@ -4,9 +4,20 @@ Entity +class Domain(Entity): + def __init__(self, target_endpoint: str, entity_manager: 'EntityManager', **kwargs): + super(Domain, self).__init__(target_endpoint, entity_manager, **kwargs) + relationships = self['relationships'] + if 'organization' in relationships: + self['relationships']['organization'] = ToOneRelationship.from_json_object(relationships['organization'].get('data')) + if 'shared_organizations' in relationships: + self['relationships']['shared_organizations'] \ + = ToManyRelationship.from_json_object(relationships['shared_organizations']) + + class DomainManager(EntityManager): def __init__(self, target_endpoint: str, client: 'CloudfoundryClient'): - super(DomainManager, self).__init__(target_endpoint, client, '/v3/domains') + super(DomainManager, self).__init__(target_endpoint, client, '/v3/domains', Domain) def create(self, name: str, internal: Optional[bool] = False, organization: Optional[ToOneRelationship] = None, @@ -49,7 +60,7 @@ def __create_shared_domain_url(self, domain_guid: str) -> str: def share_domain(self, domain_guid: str, organization_guids: ToManyRelationship): url = self.__create_shared_domain_url(domain_guid) - return super(DomainManager, self)._post(url, data=organization_guids) + return ToManyRelationship.from_json_object(super(DomainManager, self)._post(url, data=organization_guids)) def unshare_domain(self, domain_guid: str, org_guid: str): url = '{uri}/{org}'.format(uri=self.__create_shared_domain_url(domain_guid), diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index f93cf3b..60fc20b 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -1,6 +1,6 @@ import functools import logging -from typing import Any, Generator, Optional, List, Tuple +from typing import Any, Generator, Optional, List, Tuple, Union, TypeVar from urllib.parse import quote from requests import Response @@ -13,8 +13,8 @@ class Entity(JsonObject): - def __init__(self, entity_manager: 'EntityManager', *args, **kwargs): - super(Entity, self).__init__(*args, **kwargs) + def __init__(self, target_endpoint: str, client: 'CloudFoundryClient', **kwargs): + super(Entity, self).__init__(**kwargs) try: def default_method(m, u): raise NotImplementedError('Unknown method %s for url %s' % (m, u)) @@ -23,15 +23,24 @@ def default_method(m, u): if link_name != 'self': link_method = link.get('method', 'GET').lower() ref = link['href'] + manager_name = link_name if link_name.endswith('s') else '%ss' % link_name + try: + other_manager = getattr(client.v3, manager_name) + except AttributeError: + # generic manager + other_manager = EntityManager( + target_endpoint, + client, + '') if link_method == 'get': - new_method = functools.partial(entity_manager._paginate, ref) if link_name.endswith('s') \ - else functools.partial(entity_manager._get, ref) + new_method = functools.partial(other_manager._paginate, ref) if link_name.endswith('s') \ + else functools.partial(other_manager._get, ref) elif link_method == 'post': - new_method = functools.partial(entity_manager._post, ref) + new_method = functools.partial(other_manager._post, ref) elif link_method == 'put': - new_method = functools.partial(entity_manager._put, ref) + new_method = functools.partial(other_manager._put, ref) elif link_method == 'delete': - new_method = functools.partial(entity_manager._delete, ref) + new_method = functools.partial(other_manager._delete, ref) else: new_method = functools.partial(default_method, link_method, ref) new_method.__name__ = link_name @@ -44,62 +53,80 @@ def default_method(m, u): class Relationship(JsonObject): - def __init__(self, guid: str): + def __init__(self, guid: Optional[str]): super(Relationship, self).__init__(guid=guid) class ToOneRelationship(JsonObject): - def __init__(self, guid: str): + @staticmethod + def from_json_object(to_one_relationship: JsonObject): + return ToOneRelationship(None) if to_one_relationship is None \ + else ToOneRelationship(to_one_relationship['guid']) + + def __init__(self, guid: Optional[str]): super(ToOneRelationship, self).__init__(data=Relationship(guid)) + self.guid = guid class ToManyRelationship(JsonObject): + @staticmethod + def from_json_object(to_many_relations: JsonObject): + return ToManyRelationship( + *[relation['guid'] for relation in to_many_relations['data']]) + def __init__(self, *guids: str): super(ToManyRelationship, self).__init__(data=[Relationship(guid) for guid in guids]) + self.guids = list(guids) + + +ENTITY_TYPE = TypeVar('ENTITY_TYPE', bound=Entity) class EntityManager(object): - def __init__(self, target_endpoint: str, client: 'CloudFoundryClient', entity_uri: str): + def __init__(self, target_endpoint: str, client: 'CloudFoundryClient', entity_uri: str, + entity_type: ENTITY_TYPE = Entity): self.target_endpoint = target_endpoint self.entity_uri = entity_uri self.client = client + self.entity_type = entity_type - def _post(self, url: str, data: Optional[dict] = None, files: Any = None) -> Entity: + def _post(self, url: str, data: Optional[dict] = None, files: Any = None, + entity_type: ENTITY_TYPE = None) -> Entity: response = self.client.post(url, json=data, files=files) _logger.debug('POST - %s - %s', url, response.text) - return self._read_response(response) + return self._read_response(response, entity_type) - def _get(self, url: str) -> Entity: + def _get(self, url: str, entity_type: Optional[ENTITY_TYPE] = None) -> Entity: response = self.client.get(url) _logger.debug('GET - %s - %s', url, response.text) - return self._read_response(response) + return self._read_response(response, entity_type) - def _put(self, url: str, data: dict) -> Entity: + def _put(self, url: str, data: dict, entity_type: Optional[ENTITY_TYPE] = None) -> Entity: response = self.client.put(url, json=data) _logger.debug('PUT - %s - %s', url, response.text) - return self._read_response(response) + return self._read_response(response, entity_type) - def _patch(self, url: str, data: dict) -> Entity: + def _patch(self, url: str, data: dict, entity_type: Optional[ENTITY_TYPE] = None) -> Entity: response = self.client.patch(url, json=data) _logger.debug('PATCH - %s - %s', url, response.text) - return self._read_response(response) + return self._read_response(response, entity_type) def _delete(self, url: str): response = self.client.delete(url) _logger.debug('DELETE - %s - %s', url, response.text) - def _list(self, requested_path: str, **kwargs) -> PaginateEntities: + def _list(self, requested_path: str, entity_type: Optional[ENTITY_TYPE] = None, **kwargs) -> PaginateEntities: url_requested = EntityManager._get_url_filtered('%s%s' % (self.target_endpoint, requested_path), **kwargs) - for element in self._paginate(url_requested): + for element in self._paginate(url_requested, entity_type): yield element - def _paginate(self, url_requested: str) -> PaginateEntities: + def _paginate(self, url_requested: str, entity_type: Optional[ENTITY_TYPE] = None) -> PaginateEntities: response = self.client.get(url_requested) while True: _logger.debug('GET - %s - %s', url_requested, response.text) - response_json = self._read_response(response) + response_json = self._read_response(response, JsonObject) for resource in response_json['resources']: - yield self._entity(resource) + yield self._entity(resource, entity_type) if 'next' not in response_json['pagination'] \ or response_json['pagination']['next'] is None \ or response_json['pagination']['next'].get('href') is None: @@ -147,17 +174,17 @@ def get(self, entity_id: str, *extra_paths) -> Entity: requested_path = '%s%s/%s/%s' % (self.target_endpoint, self.entity_uri, entity_id, '/'.join(extra_paths)) return self._get(requested_path) - def _read_response(self, response: Response) -> Entity: + def _read_response(self, response: Response, entity_type: Optional[ENTITY_TYPE]) -> Union[JsonObject, Entity]: result = response.json(object_pairs_hook=JsonObject) - return self._entity(result) + return self._entity(result, entity_type) @staticmethod def _request(**mandatory_parameters) -> Request: return Request(**mandatory_parameters) - def _entity(self, result: dict): + def _entity(self, result: JsonObject, entity_type: Optional[ENTITY_TYPE]) -> Union[JsonObject, Entity]: if 'guid' in result: - return Entity(self, **result) + return (entity_type or self.entity_type)(self.target_endpoint, self.client, **result) else: return result diff --git a/main/cloudfoundry_client/v3/feature_flags.py b/main/cloudfoundry_client/v3/feature_flags.py index 099d8ae..440a885 100644 --- a/main/cloudfoundry_client/v3/feature_flags.py +++ b/main/cloudfoundry_client/v3/feature_flags.py @@ -1,13 +1,13 @@ from typing import Optional -from cloudfoundry_client.v3.entities import EntityManager +from cloudfoundry_client.v3.entities import EntityManager, Entity class FeatureFlagManager(EntityManager): def __init__(self, target_endpoint: str, client: 'CloudfoundryClient'): super(FeatureFlagManager, self).__init__(target_endpoint, client, '/v3/feature_flags') - def update(self, name: str, enabled: Optional[bool] = True, custom_error_message: Optional[str] = None): + def update(self, name: str, enabled: Optional[bool] = True, custom_error_message: Optional[str] = None) -> Entity: data = { 'enabled': enabled, 'custom_error_message': custom_error_message diff --git a/test/v3/test_domains.py b/test/v3/test_domains.py index bc32e92..61af12b 100644 --- a/test/v3/test_domains.py +++ b/test/v3/test_domains.py @@ -5,6 +5,7 @@ import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase +from cloudfoundry_client.v3.domains import Domain from cloudfoundry_client.v3.entities import Entity, ToManyRelationship, ToOneRelationship from fake_requests import mock_response @@ -27,6 +28,8 @@ def test_list(self): self.assertEqual(1, len(all_domains)) self.assertEqual(all_domains[0]['name'], "test-domain.com") self.assertIsInstance(all_domains[0], Entity) + for domain in all_domains: + self.assertIsInstance(domain, Domain) def test_get(self): self.client.get.return_value = mock_response( @@ -37,6 +40,7 @@ def test_get(self): result = self.client.v3.domains.get('domain_id') self.client.get.assert_called_with(self.client.get.return_value.url) self.assertIsNotNone(result) + self.assertIsInstance(result, Domain) def test_update(self): self.client.patch.return_value = mock_response( @@ -52,6 +56,7 @@ def test_update(self): } }) self.assertIsNotNone(result) + self.assertIsInstance(result, Domain) def test_create(self): self.client.post.return_value = mock_response( @@ -81,6 +86,7 @@ def test_create(self): } }) self.assertIsNotNone(result) + self.assertIsInstance(result, Domain) def test_remove(self): self.client.delete.return_value = mock_response( @@ -99,7 +105,8 @@ def test_list_domains_for_org(self): self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(1, len(all_domains)) self.assertEqual(all_domains[0]['name'], "test-domain.com") - self.assertIsInstance(all_domains[0], Entity) + for domain in all_domains: + self.assertIsInstance(domain, Domain) def test_share_domain(self): self.client.post.return_value = mock_response( @@ -118,6 +125,9 @@ def test_share_domain(self): ] }) self.assertIsNotNone(result) + self.assertIsInstance(result, ToManyRelationship) + result.guids[0] = 'organization-guid-1' + result.guids[1] = 'organization-guid-1' def test_unshare_domain(self): self.client.delete.return_value = mock_response( From c5e6ab4e467cf107d8a3cb6eacdef12610319024 Mon Sep 17 00:00:00 2001 From: Cooper Date: Thu, 5 Dec 2019 18:37:33 +0100 Subject: [PATCH 007/264] Organizations (#68) - add organizations v3 --- main/cloudfoundry_client/client.py | 3 +- main/cloudfoundry_client/main/main.py | 6 +- main/cloudfoundry_client/v3/organizations.py | 58 ++++++++++ .../GET_{id}_default_domain_response.json | 36 ++++++ ...d}_default_isolation_segment_response.json | 13 +++ .../GET_{id}_usage_summary_response.json | 14 +++ ...gn_default_isolation_segment_response.json | 13 +++ .../v3/organizations/PATCH_{id}_response.json | 29 +++++ .../v3/organizations/POST_response.json | 29 +++++ test/v2/test_organizations.py | 23 ---- test/v3/test_organizations.py | 109 ++++++++++++++++++ 11 files changed, 307 insertions(+), 26 deletions(-) create mode 100644 main/cloudfoundry_client/v3/organizations.py create mode 100644 test/fixtures/v3/organizations/GET_{id}_default_domain_response.json create mode 100644 test/fixtures/v3/organizations/GET_{id}_default_isolation_segment_response.json create mode 100644 test/fixtures/v3/organizations/GET_{id}_usage_summary_response.json create mode 100644 test/fixtures/v3/organizations/PATCH_{id}_assign_default_isolation_segment_response.json create mode 100644 test/fixtures/v3/organizations/PATCH_{id}_response.json create mode 100644 test/fixtures/v3/organizations/POST_response.json diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 77aeffc..b7db400 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -26,6 +26,7 @@ from cloudfoundry_client.v3.domains import DomainManager from cloudfoundry_client.v3.entities import EntityManager as EntityManagerV3 from cloudfoundry_client.v3.feature_flags import FeatureFlagManager +from cloudfoundry_client.v3.organizations import OrganizationManager from cloudfoundry_client.v3.tasks import TaskManager _logger = logging.getLogger(__name__) @@ -75,7 +76,7 @@ def __init__(self, target_endpoint: str, credential_manager: 'CloudFoundryClient self.domains = DomainManager(target_endpoint, credential_manager) self.feature_flags = FeatureFlagManager(target_endpoint, credential_manager) self.spaces = EntityManagerV3(target_endpoint, credential_manager, '/v3/spaces') - self.organizations = EntityManagerV3(target_endpoint, credential_manager, '/v3/organizations') + self.organizations = OrganizationManager(target_endpoint, credential_manager) self.service_instances = EntityManagerV3(target_endpoint, credential_manager, '/v3/service_instances') self.tasks = TaskManager(target_endpoint, credential_manager) diff --git a/main/cloudfoundry_client/main/main.py b/main/cloudfoundry_client/main/main.py index d658d41..1655d3d 100644 --- a/main/cloudfoundry_client/main/main.py +++ b/main/cloudfoundry_client/main/main.py @@ -168,8 +168,10 @@ def main(): logging.getLogger("urllib3").setLevel(logging.WARNING) commands = [ - CommandDomain(display_name='Organizations', entity_name='organization', filter_list_parameters=[], - allow_retrieve_by_name=True, allow_creation=True, allow_deletion=True), + CommandDomain(display_name='Organizations', entity_name='organization', + api_version='v3', + filter_list_parameters=[], allow_retrieve_by_name=True, + allow_creation=True, allow_deletion=True), CommandDomain(display_name='Spaces', entity_name='space', filter_list_parameters=['organization_guid'], allow_retrieve_by_name=True, allow_creation=True, allow_deletion=True), AppCommandDomain(), diff --git a/main/cloudfoundry_client/v3/organizations.py b/main/cloudfoundry_client/v3/organizations.py new file mode 100644 index 0000000..a8bee7b --- /dev/null +++ b/main/cloudfoundry_client/v3/organizations.py @@ -0,0 +1,58 @@ +from typing import Optional + +from cloudfoundry_client.v3.entities import EntityManager, Entity + + +class OrganizationManager(EntityManager): + def __init__(self, target_endpoint: str, client: 'CloudfoundryClient'): + super(OrganizationManager, self).__init__(target_endpoint, client, '/v3/organizations') + + def create(self, name: str, suspended: bool, + meta_labels: Optional[dict] = None, + meta_annotations: Optional[dict] = None) -> Entity: + data = { + 'name': name, + 'suspended': suspended, + 'metadata': { + 'labels': meta_labels, + 'annotations': meta_annotations + } + } + return super(OrganizationManager, self)._create(data) + + def update(self, guid: str, name: str, suspended: Optional[bool], + meta_labels: Optional[dict] = None, + meta_annotations: Optional[dict] = None) -> Entity: + data = { + 'name': name, + 'suspended': suspended, + 'metadata': { + 'labels': meta_labels, + 'annotations': meta_annotations + } + } + return super(OrganizationManager, self)._update(guid, data) + + def remove(self, guid: str): + super(OrganizationManager, self)._remove(guid) + + def assign_default_isolation_segment(self, org_guid: str, iso_seg_guid: str) -> Entity: + url = '{endpoint}{entity}/{guid}/relationships/default_isolation_segment' \ + ''.format(endpoint=self.target_endpoint, + entity=self.entity_uri, + guid=org_guid) + data = { + "data": { + "guid": iso_seg_guid + } + } + return super(OrganizationManager, self)._patch(url, data) + + def get_default_isolation_segment(self, guid: str) -> Entity: + return super(OrganizationManager, self).get(guid, 'relationships', 'default_isolation_segment') + + def get_default_domain(self, guid: str) -> Entity: + return super(OrganizationManager, self).get(guid, 'domains', 'default') + + def get_usage_summary(self, guid: str) -> Entity: + return super(OrganizationManager, self).get(guid, 'usage_summary') diff --git a/test/fixtures/v3/organizations/GET_{id}_default_domain_response.json b/test/fixtures/v3/organizations/GET_{id}_default_domain_response.json new file mode 100644 index 0000000..c8c5e7a --- /dev/null +++ b/test/fixtures/v3/organizations/GET_{id}_default_domain_response.json @@ -0,0 +1,36 @@ +{ + "guid": "3a5d3d89-3f89-4f05-8188-8a2b298c79d5", + "created_at": "2019-03-08T01:06:19Z", + "updated_at": "2019-03-08T01:06:19Z", + "name": "test-domain.com", + "internal": false, + "metadata": { + "labels": { }, + "annotations": { } + }, + "relationships": { + "organization": { + "data": { "guid": "3a3f3d89-3f89-4f05-8188-751b298c79d5" } + }, + "shared_organizations": { + "data": [ + {"guid": "404f3d89-3f89-6z72-8188-751b298d88d5"}, + {"guid": "416d3d89-3f89-8h67-2189-123b298d3592"} + ] + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5" + }, + "organization": { + "href": "https://api.example.org/v3/organizations/3a3f3d89-3f89-4f05-8188-751b298c79d5" + }, + "route_reservations": { + "href": "https://api.example.org/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5/route_reservations" + }, + "shared_organizations": { + "href": "https://api.example.org/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5/relationships/shared_organizations" + } + } +} \ No newline at end of file diff --git a/test/fixtures/v3/organizations/GET_{id}_default_isolation_segment_response.json b/test/fixtures/v3/organizations/GET_{id}_default_isolation_segment_response.json new file mode 100644 index 0000000..c585f23 --- /dev/null +++ b/test/fixtures/v3/organizations/GET_{id}_default_isolation_segment_response.json @@ -0,0 +1,13 @@ +{ + "data": { + "guid": "9d8e007c-ce52-4ea7-8a57-f2825d2c6b39" + }, + "links": { + "self": { + "href": "https://api.example.org/v3/organizations/d4c91047-7b29-4fda-b7f9-04033e5c9c9f/relationships/default_isolation_segment" + }, + "related": { + "href": "https://api.example.org/v3/isolation_segments/9d8e007c-ce52-4ea7-8a57-f2825d2c6b39" + } + } +} \ No newline at end of file diff --git a/test/fixtures/v3/organizations/GET_{id}_usage_summary_response.json b/test/fixtures/v3/organizations/GET_{id}_usage_summary_response.json new file mode 100644 index 0000000..b7a31e7 --- /dev/null +++ b/test/fixtures/v3/organizations/GET_{id}_usage_summary_response.json @@ -0,0 +1,14 @@ +{ + "usage_summary": { + "started_instances": 3, + "memory_in_mb": 50 + }, + "links": { + "self": { + "href": "https://api.example.org/v3/organizations/d4c91047-7b29-4fda-b7f9-04033e5c9c9f/usage_summary" + }, + "organization": { + "href": "https://api.example.org/v3/organizations/d4c91047-7b29-4fda-b7f9-04033e5c9c9f" + } + } +} \ No newline at end of file diff --git a/test/fixtures/v3/organizations/PATCH_{id}_assign_default_isolation_segment_response.json b/test/fixtures/v3/organizations/PATCH_{id}_assign_default_isolation_segment_response.json new file mode 100644 index 0000000..c585f23 --- /dev/null +++ b/test/fixtures/v3/organizations/PATCH_{id}_assign_default_isolation_segment_response.json @@ -0,0 +1,13 @@ +{ + "data": { + "guid": "9d8e007c-ce52-4ea7-8a57-f2825d2c6b39" + }, + "links": { + "self": { + "href": "https://api.example.org/v3/organizations/d4c91047-7b29-4fda-b7f9-04033e5c9c9f/relationships/default_isolation_segment" + }, + "related": { + "href": "https://api.example.org/v3/isolation_segments/9d8e007c-ce52-4ea7-8a57-f2825d2c6b39" + } + } +} \ No newline at end of file diff --git a/test/fixtures/v3/organizations/PATCH_{id}_response.json b/test/fixtures/v3/organizations/PATCH_{id}_response.json new file mode 100644 index 0000000..33f7326 --- /dev/null +++ b/test/fixtures/v3/organizations/PATCH_{id}_response.json @@ -0,0 +1,29 @@ +{ + "guid": "24637893-3b77-489d-bb79-8466f0d88b52", + "created_at": "2017-02-01T01:33:58Z", + "updated_at": "2017-02-01T01:33:58Z", + "name": "my-organization", + "suspended": false, + "relationships": { + "quota": { + "data": { + "guid": "b7887f5c-34bb-40c5-9778-577572e4fb2d" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/organizations/24637893-3b77-489d-bb79-8466f0d88b52" + }, + "domains": { + "href": "https://api.example.org/v3/organizations/24637893-3b77-489d-bb79-8466f0d88b52/domains" + }, + "default_domain": { + "href": "https://api.example.org/v3/organizations/24637893-3b77-489d-bb79-8466f0d88b52/domains/default" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } +} \ No newline at end of file diff --git a/test/fixtures/v3/organizations/POST_response.json b/test/fixtures/v3/organizations/POST_response.json new file mode 100644 index 0000000..33f7326 --- /dev/null +++ b/test/fixtures/v3/organizations/POST_response.json @@ -0,0 +1,29 @@ +{ + "guid": "24637893-3b77-489d-bb79-8466f0d88b52", + "created_at": "2017-02-01T01:33:58Z", + "updated_at": "2017-02-01T01:33:58Z", + "name": "my-organization", + "suspended": false, + "relationships": { + "quota": { + "data": { + "guid": "b7887f5c-34bb-40c5-9778-577572e4fb2d" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/organizations/24637893-3b77-489d-bb79-8466f0d88b52" + }, + "domains": { + "href": "https://api.example.org/v3/organizations/24637893-3b77-489d-bb79-8466f0d88b52/domains" + }, + "default_domain": { + "href": "https://api.example.org/v3/organizations/24637893-3b77-489d-bb79-8466f0d88b52/domains/default" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } +} \ No newline at end of file diff --git a/test/v2/test_organizations.py b/test/v2/test_organizations.py index ec5455a..300e79b 100644 --- a/test/v2/test_organizations.py +++ b/test/v2/test_organizations.py @@ -1,10 +1,8 @@ -import sys import unittest from functools import reduce from http import HTTPStatus from unittest.mock import call, patch -import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase from fake_requests import mock_response @@ -55,24 +53,3 @@ def test_entity(self): self.client.get.assert_has_calls([call(side_effect.url) for side_effect in self.client.get.side_effect], any_order=False) - @patch.object(sys, 'argv', ['main', 'list_organizations']) - def test_main_list_organizations(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = mock_response('/v2/organizations', - HTTPStatus.OK, - None, - 'v2', 'organizations', 'GET_response.json') - main.main() - self.client.get.assert_called_with(self.client.get.return_value.url) - - @patch.object(sys, 'argv', ['main', 'get_organization', 'fe79371b-39b8-4f0d-8331-cff423a06aca']) - def test_main_get_organization(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = mock_response('/v2/organizations/fe79371b-39b8-4f0d-8331-cff423a06aca', - HTTPStatus.OK, - None, - 'v2', 'organizations', 'GET_{id}_response.json') - main.main() - self.client.get.assert_called_with(self.client.get.return_value.url) diff --git a/test/v3/test_organizations.py b/test/v3/test_organizations.py index 4519134..dfe8d4f 100644 --- a/test/v3/test_organizations.py +++ b/test/v3/test_organizations.py @@ -1,6 +1,9 @@ +import sys import unittest from http import HTTPStatus +from unittest.mock import patch +import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase from cloudfoundry_client.v3.entities import Entity from fake_requests import mock_response @@ -35,4 +38,110 @@ def test_get(self): self.assertEqual("my-organization", organization['name']) self.assertIsInstance(organization, Entity) + def test_update(self): + self.client.patch.return_value = mock_response( + '/v3/organizations/organization_id', + HTTPStatus.OK, + None, + 'v3', 'organizations', 'PATCH_{id}_response.json') + result = self.client.v3.organizations.update('organization_id', 'my-organization', + suspended=True) + self.client.patch.assert_called_with(self.client.patch.return_value.url, + json={'suspended': True, + 'name': 'my-organization', + 'metadata': { + 'labels': None, + 'annotations': None + } + }) + self.assertIsNotNone(result) + def test_create(self): + self.client.post.return_value = mock_response( + '/v3/organizations', + HTTPStatus.OK, + None, + 'v3', 'organizations', 'POST_response.json') + result = self.client.v3.organizations.create('my-organization', + suspended=False) + self.client.post.assert_called_with(self.client.post.return_value.url, + files=None, + json={'name': 'my-organization', + 'suspended': False, + 'metadata': { + 'labels': None, + 'annotations': None + } + }) + self.assertIsNotNone(result) + + def test_remove(self): + self.client.delete.return_value = mock_response( + '/v3/organizations/organization_id', + HTTPStatus.NO_CONTENT, + None) + self.client.v3.organizations.remove('organization_id') + self.client.delete.assert_called_with(self.client.delete.return_value.url) + + def test_assign_default_isolation_segment(self): + self.client.patch.return_value = mock_response( + '/v3/organizations/organization_id/relationships/default_isolation_segment', + HTTPStatus.OK, + None, + 'v3', 'organizations', 'PATCH_{id}_assign_default_isolation_segment_response.json') + result = self.client.v3.organizations.assign_default_isolation_segment( + 'organization_id', 'iso_seg_guid') + self.client.patch.assert_called_with(self.client.patch.return_value.url, + json={'data': { + 'guid': 'iso_seg_guid' + } + }) + self.assertIsNotNone(result) + + def test_get_default_isolation_segment(self): + self.client.get.return_value = mock_response('/v3/organizations/organization_id/relationships/default_isolation_segment', + HTTPStatus.OK, + None, + 'v3', 'organizations', 'GET_{id}_default_isolation_segment_response.json') + self.client.v3.organizations.get_default_isolation_segment('organization_id') + self.client.get.assert_called_with(self.client.get.return_value.url) + + def test_get_default_domain(self): + self.client.get.return_value = mock_response('/v3/organizations/organization_id/domains/default', + HTTPStatus.OK, + None, + 'v3', 'organizations', 'GET_{id}_default_domain_response.json') + default_domain = self.client.v3.organizations.get_default_domain('organization_id') + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual("test-domain.com", default_domain['name']) + self.assertIsInstance(default_domain, Entity) + + def test_get_usage_summary(self): + self.client.get.return_value = mock_response('/v3/organizations/organization_id/usage_summary', + HTTPStatus.OK, + None, + 'v3', 'organizations', 'GET_{id}_usage_summary_response.json') + self.client.v3.organizations.get_usage_summary('organization_id') + self.client.get.assert_called_with(self.client.get.return_value.url) + + @patch.object(sys, 'argv', ['main', 'list_organizations']) + def test_main_list_organizations(self): + with patch('cloudfoundry_client.main.main.build_client_from_configuration', + new=lambda: self.client): + self.client.get.return_value = mock_response('/v3/organizations', + HTTPStatus.OK, + None, + 'v3', 'organizations', 'GET_response.json') + main.main() + self.client.get.assert_called_with(self.client.get.return_value.url) + + @patch.object(sys, 'argv', ['main', 'get_organization', '24637893-3b77-489d-bb79-8466f0d88b52']) + def test_main_get_organizations(self): + with patch('cloudfoundry_client.main.main.build_client_from_configuration', + new=lambda: self.client): + self.client.get.return_value = mock_response('/v3/organizations/24637893-3b77-489d-bb79-8466f0d88b52', + HTTPStatus.OK, + None, + 'v3', 'organizations', 'GET_{id}_response.json') + main.main() + self.client.get.assert_called_with(self.client.get.return_value.url) From 3e8fb59fbd800e7f93a129a6580f5b16e30774a8 Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Thu, 5 Dec 2019 18:55:02 +0100 Subject: [PATCH 008/264] Add action file --- .github/workflows/pythonapp.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/pythonapp.yml diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml new file mode 100644 index 0000000..613e627 --- /dev/null +++ b/.github/workflows/pythonapp.yml @@ -0,0 +1,22 @@ +name: Python application + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: Set up Python 3.7 + uses: actions/setup-python@v1 + with: + python-version: 3.7 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + - name: Run tests + run: | + python setup.py test From bfb8df5a60c908a1500a88e4bfd96a6b092b0dde Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Thu, 5 Dec 2019 18:53:31 +0100 Subject: [PATCH 009/264] Remove useless 'mock' dependency --- setup.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/setup.py b/setup.py index 1ed52dc..e18f877 100644 --- a/setup.py +++ b/setup.py @@ -75,8 +75,5 @@ def run(self): cmdclass=dict(generate=GenerateCommand), package_dir={package_directory: '%s/%s' % (src_dir, package_directory)}, install_requires=[requirement.rstrip(' \r\n') for requirement in open('requirements.txt')], - tests_require=[ - 'mock==2.0.0', - ], test_suite='test', ) From 5da07427db9ab16a8554682965d0c7a14983fc3f Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Thu, 5 Dec 2019 19:03:01 +0100 Subject: [PATCH 010/264] Change to python package --- .../workflows/{pythonapp.yml => pythonpackage.yml} | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) rename .github/workflows/{pythonapp.yml => pythonpackage.yml} (57%) diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonpackage.yml similarity index 57% rename from .github/workflows/pythonapp.yml rename to .github/workflows/pythonpackage.yml index 613e627..127533c 100644 --- a/.github/workflows/pythonapp.yml +++ b/.github/workflows/pythonpackage.yml @@ -1,4 +1,4 @@ -name: Python application +name: Python package on: [push] @@ -6,17 +6,22 @@ jobs: build: runs-on: ubuntu-latest + strategy: + max-parallel: 1 + matrix: + python-version: [3.6, 3.7] steps: - uses: actions/checkout@v1 - - name: Set up Python 3.7 + - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: - python-version: 3.7 + python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt - - name: Run tests + - name: Launch tests run: | python setup.py test + From 2b696778b55a5da4a62e5547fba19e3dd48a4325 Mon Sep 17 00:00:00 2001 From: Cooper Date: Fri, 6 Dec 2019 09:19:44 +0100 Subject: [PATCH 011/264] Network policies (#67) Implement network policies --- README.rst | 28 ++++ main/cloudfoundry_client/client.py | 7 + .../networking/entities.py | 124 ++++++++++++++++++ .../networking/v1/external/policies.py | 79 +++++++++++ .../networking/v1/external/tags.py | 0 .../v1/external/policies/GET_response.json | 31 +++++ test/networking/__init__.py | 0 test/networking/v1/__init__.py | 0 test/networking/v1/external/__init__.py | 0 test/networking/v1/external/test_policies.py | 46 +++++++ 10 files changed, 315 insertions(+) create mode 100644 main/cloudfoundry_client/networking/entities.py create mode 100644 main/cloudfoundry_client/networking/v1/external/policies.py create mode 100644 main/cloudfoundry_client/networking/v1/external/tags.py create mode 100644 test/fixtures/networking/v1/external/policies/GET_response.json create mode 100644 test/networking/__init__.py create mode 100644 test/networking/v1/__init__.py create mode 100644 test/networking/v1/external/__init__.py create mode 100644 test/networking/v1/external/test_policies.py diff --git a/README.rst b/README.rst index 3e36896..ec89d6c 100644 --- a/README.rst +++ b/README.rst @@ -208,6 +208,34 @@ Available managers on API V3 are: The managers provide the same methods as the V2 managers. +Networking +------ + +policy server +~~~~~~~~ + +At the moment we have only the network policies implemented + +.. code-block:: python + + for policy in client.network.v1.external.policies.list(): + print('destination protocol = {}'.format(policy['destination']['protocol'])) + print('destination from port = {}'.format(policy['destination']['ports']['start'])) + print('destination to port = {}'.format(policy['destination']['ports']['end'])) + + +Available managers on API V3 are: + +- ``policy`` + +This manager provides: + +- ``list(**kwargs)``: return an *iterator* on entities, according to the given filtered parameters +- ``__iter__``: iteration on the manager itself. Alias for a no-filter list +- ``_create``: the create operation. Since it is a generic operation (only takes a *dict* object), this operation is protected +- ``_remove``: the delete operation. This operation is maintained protected. + + Application logs ---------------- diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index b7db400..92b7a9d 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -7,6 +7,7 @@ from requests import Response from cloudfoundry_client.doppler.client import DopplerClient +from cloudfoundry_client.networking.v1.external.policies import PolicyManager from cloudfoundry_client.errors import InvalidStatusCode from cloudfoundry_client.v2.apps import AppManager as AppManagerV2 from cloudfoundry_client.v2.buildpacks import BuildpackManager as BuildpackManagerV2 @@ -40,6 +41,11 @@ def __init__(self, api_version: str, authorization_endpoint: str, api_endpoint: self.doppler_endpoint = doppler_endpoint +class NetworkingV1External(object): + def __init__(self, target_endpoint: str, credential_manager: 'CloudFoundryClient'): + self.policies = PolicyManager(target_endpoint, credential_manager) + + class V2(object): def __init__(self, target_endpoint: str, credential_manager: 'CloudFoundryClient'): self.apps = AppManagerV2(target_endpoint, credential_manager) @@ -116,6 +122,7 @@ def __init__(self, target_endpoint: str, client_id: str = 'cf', client_secret: s 'http' if info.doppler_endpoint.startswith('ws://') else 'https'], self.service_information.verify, self) if info.doppler_endpoint is not None else None + self.networking_v1_external = NetworkingV1External(target_endpoint, self) self.info = info @property diff --git a/main/cloudfoundry_client/networking/entities.py b/main/cloudfoundry_client/networking/entities.py new file mode 100644 index 0000000..845756b --- /dev/null +++ b/main/cloudfoundry_client/networking/entities.py @@ -0,0 +1,124 @@ +import logging +from functools import partial, reduce +from typing import Callable, List, Tuple, Any, Optional, Generator +from urllib.parse import quote + +from requests import Response + +from cloudfoundry_client.errors import InvalidEntity +from cloudfoundry_client.json_object import JsonObject +from cloudfoundry_client.request_object import Request + +_logger = logging.getLogger(__name__) + + +class Entity(JsonObject): + def __init__(self, target_endpoint: str, client: 'CloudFoundryClient', *args, **kwargs): + super(Entity, self).__init__(*args, **kwargs) + self.target_endpoint = target_endpoint + self.client = client + try: + src = self['source'] + dst = self['destination'] + src['id'] + dst['id'] + dst['protocol'] + dst['ports']['start'] + dst['ports']['end'] + except KeyError: + raise InvalidEntity(**self) + + +EntityBuilder = Callable[[List[Tuple[str, Any]]], Entity] + + +class EntityManager(object): + list_query_parameters = ['page', 'results-per-page', 'order-direction'] + + list_multi_parameters = ['order-by'] + + def __init__(self, target_endpoint: str, client: 'CloudFoundryClient', entity_uri: str, + entity_builder: Optional[EntityBuilder] = None): + self.target_endpoint = target_endpoint + self.entity_uri = entity_uri + self.client = client + self.entity_builder = entity_builder if entity_builder is not None else lambda pairs: Entity(target_endpoint, + client, pairs) + + def _list(self, requested_path: str, entity_builder: Optional[EntityBuilder] = None, **kwargs) \ + -> Generator[Entity, None, None]: + url_requested = self._get_url_filtered('%s%s' % (self.target_endpoint, requested_path), **kwargs) + response = self.client.get(url_requested) + entity_builder = self._get_entity_builder(entity_builder) + _logger.debug('GET - %s - %s', url_requested, response.text) + response_json = self._read_response(response, JsonObject) + for resource in response_json['policies']: + yield entity_builder(list(resource.items())) + + def _create(self, data: dict, **kwargs) -> Entity: + url = '%s%s' % (self.target_endpoint, self.entity_uri) + return self._post(url, data, **kwargs) + + def _remove(self, resource_id: str, **kwargs): + url = '%s%s/%s' % (self.target_endpoint, self.entity_uri, resource_id) + self._delete(url, **kwargs) + + def _post(self, url: str, data: Optional[dict] = None, **kwargs): + response = self.client.post(url, json=data, **kwargs) + _logger.debug('POST - %s - %s', url, response.text) + return self._read_response(response) + + def _delete(self, url: str, **kwargs): + response = self.client.delete(url, **kwargs) + _logger.debug('DELETE - %s - %s', url, response.text) + + def __iter__(self) -> Generator[Entity, None, None]: + return self.list() + + def list(self, **kwargs) -> Generator[Entity, None, None]: + return self._list(self.entity_uri, **kwargs) + + def get_first(self, **kwargs) -> Optional[Entity]: + kwargs.setdefault('results-per-page', 1) + for entity in self._list(self.entity_uri, **kwargs): + return entity + return None + + def _read_response(self, response: Response, other_entity_builder: Optional[EntityBuilder] = None): + entity_builder = self._get_entity_builder(other_entity_builder) + result = response.json(object_pairs_hook=JsonObject) + return entity_builder(list(result.items())) + + @staticmethod + def _request(**mandatory_parameters) -> Request: + return Request(**mandatory_parameters) + + def _get_entity_builder(self, entity_builder: Optional[EntityBuilder]) -> EntityBuilder: + if entity_builder is None: + return self.entity_builder + else: + return entity_builder + + def _get_url_filtered(self, url: str, **kwargs) -> str: + + def _append_encoded_parameter(parameters: List[str], args: Tuple[str, Any]) -> List[str]: + parameter_name, parameter_value = args[0], args[1] + if parameter_name in self.list_query_parameters: + parameters.append('%s=%s' % (parameter_name, str(parameter_value))) + elif parameter_name in self.list_multi_parameters: + value_list = parameter_value + if not isinstance(value_list, (list, tuple)): + value_list = [value_list] + for value in value_list: + parameters.append('%s=%s' % (parameter_name, str(value))) + elif isinstance(parameter_value, (list, tuple)): + parameters.append('q=%s' % quote('%s IN %s' % (parameter_name, ','.join(parameter_value)))) + else: + parameters.append('q=%s' % quote('%s:%s' % (parameter_name, str(parameter_value)))) + return parameters + + if len(kwargs) > 0: + return '%s?%s' % (url, + "&".join(reduce(_append_encoded_parameter, sorted(list(kwargs.items())), []))) + else: + return url diff --git a/main/cloudfoundry_client/networking/v1/external/policies.py b/main/cloudfoundry_client/networking/v1/external/policies.py new file mode 100644 index 0000000..17bf611 --- /dev/null +++ b/main/cloudfoundry_client/networking/v1/external/policies.py @@ -0,0 +1,79 @@ +import logging +from cloudfoundry_client.networking.entities import EntityManager +from typing import List + +_logger = logging.getLogger(__name__) + + +class Policy: + def __init__(self, src_id: str, dst_id: str, proto: str, start_port: int, end_port: int): + self.source = {'id': src_id} + + self.destination = {'id': dst_id, 'ports': {}} + + __protos = ['tcp', 'udp'] + if proto.lower() in __protos: + self.destination['protocol'] = proto.lower() + else: + raise ValueError('unknown protocol {got}, known values are {known}' + ''.format(got=proto, known=__protos)) + + if 1 <= start_port <= 65535: + self.destination['ports']['start'] = start_port + else: + raise ValueError('start port is out of range') + + if 1 <= end_port <= 65535: + self.destination['ports']['end'] = end_port + else: + raise ValueError('end port is out of range') + + @classmethod + def from_dict(cls, policy: dict): + return cls(src_id=policy['source']['id'], + dst_id=policy['destination']['id'], + proto=policy['destination']['protocol'], + start_port=policy['destination']['ports']['start'], + end_port=policy['destination']['ports']['end']) + + def dump(self): + return self.__dict__ + + +class PolicyManager(EntityManager): + def __init__(self, target_endpoint, client): + super(PolicyManager, self).__init__(target_endpoint, client, '/networking/v1/external/policies') + + def create(self, policies: List[Policy]): + """create a new network policy + + Responses: + * 200 (successful) + * 400 (invalid request) + * 406 (unsupported API version) + + :param policies: the policies to create, a list of Policy objects + """ + data = list() + for policy in policies: + if not isinstance(policy, Policy): + raise TypeError + data.append(policy.dump()) + return super(PolicyManager, self)._create({'policies': data}) + + def delete(self, policies: List[Policy]): + """remove a new network policy + + Responses: + * 200 (successful) + * 400 (invalid request) + * 406 (unsupported API version) + + :param policies: the policies to create, a list of Policy objects + """ + data = list() + for policy in policies: + if not isinstance(policy, Policy): + raise TypeError + data.append(policy.dump()) + return super(PolicyManager, self)._delete({'policies': data}) diff --git a/main/cloudfoundry_client/networking/v1/external/tags.py b/main/cloudfoundry_client/networking/v1/external/tags.py new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/networking/v1/external/policies/GET_response.json b/test/fixtures/networking/v1/external/policies/GET_response.json new file mode 100644 index 0000000..30f820c --- /dev/null +++ b/test/fixtures/networking/v1/external/policies/GET_response.json @@ -0,0 +1,31 @@ +{ + "total_policies": 2, + "policies": [ + { + "source": { + "id": "1081ceac-f5c4-47a8-95e8-88e1e302efb5" + }, + "destination": { + "id": "38f08df0-19df-4439-b4e9-61096d4301ea", + "protocol": "tcp", + "ports": { + "start": 1234, + "end": 1235 + } + } + }, + { + "source": { + "id": "308e7ef1-63f1-4a6c-978c-2e527cbb1c36" + }, + "destination": { + "id": "308e7ef1-63f1-4a6c-978c-2e527cbb1c36", + "protocol": "tcp", + "ports": { + "start": 1234, + "end": 1235 + } + } + } + ] +} \ No newline at end of file diff --git a/test/networking/__init__.py b/test/networking/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/networking/v1/__init__.py b/test/networking/v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/networking/v1/external/__init__.py b/test/networking/v1/external/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/networking/v1/external/test_policies.py b/test/networking/v1/external/test_policies.py new file mode 100644 index 0000000..cd5565d --- /dev/null +++ b/test/networking/v1/external/test_policies.py @@ -0,0 +1,46 @@ +import unittest +from http import HTTPStatus + +from abstract_test_case import AbstractTestCase +from cloudfoundry_client.networking.v1.external.policies import Policy +from fake_requests import mock_response + + +class TestPolicies(unittest.TestCase, AbstractTestCase): + @classmethod + def setUpClass(cls): + cls.mock_client_class() + + def setUp(self): + self.build_client() + + def test_list(self): + self.client.get.return_value = mock_response('/networking/v1/external/policies', + HTTPStatus.OK, + None, + 'networking', 'v1', 'external', 'policies', 'GET_response.json') + all_policies = [policy for policy in self.client.networking_v1_external.policies.list()] + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual(2, len(all_policies)) + self.assertEqual(all_policies[0]['source']['id'], "1081ceac-f5c4-47a8-95e8-88e1e302efb5") + self.assertEqual(all_policies[0]['destination']['id'], "38f08df0-19df-4439-b4e9-61096d4301ea") + + def test_delete(self): + self.client.delete.return_value = mock_response('/networking/v1/external/policies/delete', + HTTPStatus.OK, + None) + policy = Policy(src_id='1081ceac-f5c4-47a8-95e8-88e1e302efb5', + dst_id='38f08df0-19df-4439-b4e9-61096d4301ea', + proto='tcp', + start_port=1234, + end_port=1234) + self.client.networking_v1_external.policies.delete([policy, ]) + self.client.delete.assert_called_with({'policies': [ + {'source': + {'id': '1081ceac-f5c4-47a8-95e8-88e1e302efb5'}, + 'destination': { + 'id': '38f08df0-19df-4439-b4e9-61096d4301ea', + 'ports': {'start': 1234, 'end': 1234}, + 'protocol': 'tcp'} + } + ]}) From 3defa4259980c2487dd7b6722a30f12a9a45c16e Mon Sep 17 00:00:00 2001 From: Cooper Date: Fri, 6 Dec 2019 16:17:57 +0100 Subject: [PATCH 012/264] fix network policies (#69) Fix cli for network policy --- main/cloudfoundry_client/main/main.py | 6 +++--- main/cloudfoundry_client/networking/__init__.py | 0 main/cloudfoundry_client/networking/v1/__init__.py | 0 main/cloudfoundry_client/networking/v1/external/__init__.py | 0 4 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 main/cloudfoundry_client/networking/__init__.py create mode 100644 main/cloudfoundry_client/networking/v1/__init__.py create mode 100644 main/cloudfoundry_client/networking/v1/external/__init__.py diff --git a/main/cloudfoundry_client/main/main.py b/main/cloudfoundry_client/main/main.py index 1655d3d..bd9a016 100644 --- a/main/cloudfoundry_client/main/main.py +++ b/main/cloudfoundry_client/main/main.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 import argparse import json import logging @@ -170,7 +170,7 @@ def main(): commands = [ CommandDomain(display_name='Organizations', entity_name='organization', api_version='v3', - filter_list_parameters=[], allow_retrieve_by_name=True, + filter_list_parameters=['names', 'guids'], allow_retrieve_by_name=True, allow_creation=True, allow_deletion=True), CommandDomain(display_name='Spaces', entity_name='space', filter_list_parameters=['organization_guid'], allow_retrieve_by_name=True, allow_creation=True, allow_deletion=True), @@ -196,7 +196,7 @@ def main(): allow_retrieve_by_name=False, allow_creation=True, allow_deletion=True), CommandDomain(display_name='Buildpacks', entity_name='buildpack', api_version='v3', - filter_list_parameters=[], allow_retrieve_by_name=True, + filter_list_parameters=['names', 'stacks'], allow_retrieve_by_name=True, allow_creation=True, allow_deletion=True), CommandDomain(display_name='Domains', entity_name='domain', api_version='v3', diff --git a/main/cloudfoundry_client/networking/__init__.py b/main/cloudfoundry_client/networking/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/main/cloudfoundry_client/networking/v1/__init__.py b/main/cloudfoundry_client/networking/v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/main/cloudfoundry_client/networking/v1/external/__init__.py b/main/cloudfoundry_client/networking/v1/external/__init__.py new file mode 100644 index 0000000..e69de29 From ee74a94e98bbe79905649ed38af530392df5900c Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sat, 28 Dec 2019 12:54:21 +0100 Subject: [PATCH 013/264] Enforce entity attribute check This change enforce type checking for entity attribute in object returned See #56 --- main/cloudfoundry_client/v2/entities.py | 3 +- ...valid_entity_with_invalid_entity_type.json | 9 +++ .../GET_invalid_entity_with_null_entity.json | 9 +++ .../GET_invalid_entity_without_entity.json | 8 +++ test/v2/test_entities.py | 64 +++++++++++++++---- 5 files changed, 81 insertions(+), 12 deletions(-) create mode 100644 test/fixtures/fake/GET_invalid_entity_with_invalid_entity_type.json create mode 100644 test/fixtures/fake/GET_invalid_entity_with_null_entity.json create mode 100644 test/fixtures/fake/GET_invalid_entity_without_entity.json diff --git a/main/cloudfoundry_client/v2/entities.py b/main/cloudfoundry_client/v2/entities.py index 3db4480..cbaf34b 100644 --- a/main/cloudfoundry_client/v2/entities.py +++ b/main/cloudfoundry_client/v2/entities.py @@ -18,7 +18,7 @@ def __init__(self, target_endpoint: str, client: 'CloudFoundryClient', *args, ** self.target_endpoint = target_endpoint self.client = client try: - if 'entity' not in self: + if not (isinstance(self.get('entity'), dict)): raise InvalidEntity(**self) for attribute, value in list(self['entity'].items()): @@ -48,6 +48,7 @@ def __init__(self, target_endpoint: str, client: 'CloudFoundryClient', *args, ** PaginateEntities = Generator[Entity, None, None] + class EntityManager(object): list_query_parameters = ['page', 'results-per-page', 'order-direction'] diff --git a/test/fixtures/fake/GET_invalid_entity_with_invalid_entity_type.json b/test/fixtures/fake/GET_invalid_entity_with_invalid_entity_type.json new file mode 100644 index 0000000..1269121 --- /dev/null +++ b/test/fixtures/fake/GET_invalid_entity_with_invalid_entity_type.json @@ -0,0 +1,9 @@ +{ + "metadata": { + "guid": "any-id", + "url": "/fake/any-id", + "created_at": "2015-11-30T23:38:33Z", + "updated_at": "2015-11-30T23:38:33Z" + }, + "entity": [] +} \ No newline at end of file diff --git a/test/fixtures/fake/GET_invalid_entity_with_null_entity.json b/test/fixtures/fake/GET_invalid_entity_with_null_entity.json new file mode 100644 index 0000000..186e6b9 --- /dev/null +++ b/test/fixtures/fake/GET_invalid_entity_with_null_entity.json @@ -0,0 +1,9 @@ +{ + "metadata": { + "guid": "any-id", + "url": "/fake/any-id", + "created_at": "2015-11-30T23:38:33Z", + "updated_at": "2015-11-30T23:38:33Z" + }, + "entity": null +} \ No newline at end of file diff --git a/test/fixtures/fake/GET_invalid_entity_without_entity.json b/test/fixtures/fake/GET_invalid_entity_without_entity.json new file mode 100644 index 0000000..d977346 --- /dev/null +++ b/test/fixtures/fake/GET_invalid_entity_without_entity.json @@ -0,0 +1,8 @@ +{ + "metadata": { + "guid": "any-id", + "url": "/fake/any-id", + "created_at": "2015-11-30T23:38:33Z", + "updated_at": "2015-11-30T23:38:33Z" + } +} \ No newline at end of file diff --git a/test/v2/test_entities.py b/test/v2/test_entities.py index dce4ce2..612b3e0 100644 --- a/test/v2/test_entities.py +++ b/test/v2/test_entities.py @@ -3,37 +3,79 @@ from http import HTTPStatus from unittest.mock import MagicMock, call +from cloudfoundry_client.errors import InvalidEntity from cloudfoundry_client.v2.entities import EntityManager from fake_requests import TARGET_ENDPOINT, mock_response class TestEntities(unittest.TestCase): + + def test_invalid_entity_without_entity_attribute(self): + client = MagicMock() + entity_manager = EntityManager(TARGET_ENDPOINT, client, '/fake/anyone') + + client.get.return_value = mock_response( + '/fake/anyone/any-id', + HTTPStatus.OK, + None, + 'fake', 'GET_invalid_entity_without_entity.json') + + self.assertRaises(InvalidEntity, lambda: entity_manager['any-id']) + + def test_invalid_entity_with_null_entity(self): + client = MagicMock() + entity_manager = EntityManager(TARGET_ENDPOINT, client, '/fake/anyone') + + client.get.return_value = mock_response( + '/fake/anyone/any-id', + HTTPStatus.OK, + None, + 'fake', 'GET_invalid_entity_with_null_entity.json') + + self.assertRaises(InvalidEntity, lambda: entity_manager['any-id']) + + def test_invalid_entity_with_invalid_entity_type(self): + client = MagicMock() + entity_manager = EntityManager(TARGET_ENDPOINT, client, '/fake/anyone') + + client.get.return_value = mock_response( + '/fake/anyone/any-id', + HTTPStatus.OK, + None, + 'fake', 'GET_invalid_entity_with_invalid_entity_type.json') + + self.assertRaises(InvalidEntity, lambda: entity_manager['any-id']) + def test_query(self): url = EntityManager('http://cf.api', None, '/v2/apps')._get_url_filtered('/v2/apps', **{'results-per-page': 20, - 'order-direction': 'asc', - 'page': 1, - 'space_guid': 'id', - 'order-by': 'id'}) + 'order-direction': 'asc', + 'page': 1, + 'space_guid': 'id', + 'order-by': 'id'}) self.assertEqual('/v2/apps?order-by=id&order-direction=asc&page=1&results-per-page=20&q=space_guid%3Aid', url) def test_query_multi_order_by(self): - url = EntityManager('http://cf.api', None, '/v2/apps')._get_url_filtered('/v2/apps', **{'order-by': ['timestamp', 'id']}) + url = EntityManager('http://cf.api', None, '/v2/apps')._get_url_filtered('/v2/apps', + **{'order-by': ['timestamp', 'id']}) self.assertEqual('/v2/apps?order-by=timestamp&order-by=id', url) def test_query_single_order_by(self): - url = EntityManager('http://cf.api', None, '/v2/apps')._get_url_filtered('/v2/apps', **{'order-by': 'timestamp'}) + url = EntityManager('http://cf.api', None, '/v2/apps')._get_url_filtered('/v2/apps', + **{'order-by': 'timestamp'}) self.assertEqual('/v2/apps?order-by=timestamp', url) def test_query_in(self): url = EntityManager('http://cf.api', None, '/v2/apps')._get_url_filtered('/v2/apps', **{'results-per-page': 20, - 'order-direction': 'asc', - 'page': 1, - 'space_guid': ['id1', 'id2']}) + 'order-direction': 'asc', + 'page': 1, + 'space_guid': ['id1', + 'id2']}) self.assertEqual('/v2/apps?order-direction=asc&page=1&results-per-page=20&q=space_guid%20IN%20id1%2Cid2', url) def test_multi_query(self): - url = EntityManager('http://cf.api', None, '/v2/events')._get_url_filtered('/v2/events', **{'type': ['create', 'update'], - 'organization_guid': 'org-id'}) + url = EntityManager('http://cf.api', None, '/v2/events')._get_url_filtered('/v2/events', + **{'type': ['create', 'update'], + 'organization_guid': 'org-id'}) self.assertEqual('/v2/events?q=organization_guid%3Aorg-id&q=type%20IN%20create%2Cupdate', url) def test_list(self): From 65c16c742d74ff67881ab596961688a05d644669 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Mon, 17 Feb 2020 17:44:36 +0100 Subject: [PATCH 014/264] Remove trailing '/' in target endpoint See #60 --- main/cloudfoundry_client/client.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 92b7a9d..ccb9bb8 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -109,20 +109,21 @@ def __init__(self, target_endpoint: str, client_id: str = 'cf', client_secret: s verify = kwargs.get('verify', True) self.token_format = kwargs.get('token_format') self.login_hint = kwargs.get('login_hint') - info = self._get_info(target_endpoint, proxy, verify=verify) + target_endpoint_trimmed = target_endpoint.rstrip('/') + info = self._get_info(target_endpoint_trimmed, proxy, verify=verify) if not info.api_version.startswith('2.'): raise AssertionError('Only version 2 is supported for now. Found %s' % info.api_version) service_information = ServiceInformation(None, '%s/oauth/token' % info.authorization_endpoint, client_id, client_secret, [], verify) super(CloudFoundryClient, self).__init__(service_information, proxies=proxy) - self.v2 = V2(target_endpoint, self) - self.v3 = V3(target_endpoint, self) + self.v2 = V2(target_endpoint_trimmed, self) + self.v3 = V3(target_endpoint_trimmed, self) self._doppler = DopplerClient(info.doppler_endpoint, self.proxies[ 'http' if info.doppler_endpoint.startswith('ws://') else 'https'], self.service_information.verify, self) if info.doppler_endpoint is not None else None - self.networking_v1_external = NetworkingV1External(target_endpoint, self) + self.networking_v1_external = NetworkingV1External(target_endpoint_trimmed, self) self.info = info @property From 5babe9f3b7a444ba3379a5bc84c3f65972507f38 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Thu, 20 Feb 2020 20:42:08 +0100 Subject: [PATCH 015/264] Add command to restart a specific instance of an application According to cf-cli, doing a DELETE on '/v2/apps/app_id/instances/instance_id' will restart the instance See #71 --- .../main/apps_command_domain.py | 19 ++++++++++++++++++- main/cloudfoundry_client/v2/apps.py | 8 +++++++- test/v2/test_apps.py | 18 ++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/main/cloudfoundry_client/main/apps_command_domain.py b/main/cloudfoundry_client/main/apps_command_domain.py index c06092e..1be117c 100644 --- a/main/cloudfoundry_client/main/apps_command_domain.py +++ b/main/cloudfoundry_client/main/apps_command_domain.py @@ -29,7 +29,9 @@ def __init__(self): (self.simple_extra_command('restage'), 'Restage an application',), (self.app_routes(), - 'List the routes(host) of an application')]) + 'List the routes(host) of an application'), + (self.restart_instance(), + 'Restart the instance of an application')]) def recent_logs(self) -> Command: def execute(client, arguments): @@ -65,6 +67,21 @@ def execute(client: CloudFoundryClient, arguments: Namespace): return Command('app_routes', self._generate_id_command_parser('app_routes'), execute) + def restart_instance(self) -> Command: + def generate_parser(parser: _SubParsersAction): + command_parser = parser.add_parser('restart_instance') + command_parser.add_argument('id', metavar='ids', type=str, nargs=1, + help='The id. Can be UUID or name (first found then)') + command_parser.add_argument('instance_id', metavar='instance_ids', type=int, nargs=1, + help='The instance id') + + def execute(client: CloudFoundryClient, arguments: Namespace): + app_domain = self._get_client_domain(client) + resource_id = self.resolve_id(arguments.id[0], lambda x: app_domain.get_first(name=x)) + getattr(app_domain, 'restart_instance')(resource_id, int(arguments.instance_id[0])) + + return Command('restart_instance', generate_parser, execute) + @staticmethod def _generate_id_command_parser(entry: str) -> Callable[[_SubParsersAction], None]: def generate_parser(parser: _SubParsersAction): diff --git a/main/cloudfoundry_client/v2/apps.py b/main/cloudfoundry_client/v2/apps.py index 4e237c1..16c0515 100644 --- a/main/cloudfoundry_client/v2/apps.py +++ b/main/cloudfoundry_client/v2/apps.py @@ -14,7 +14,7 @@ class Application(Entity): - def instances(self) -> Dict[str, dict]: + def instances(self) -> Dict[str, JsonObject]: return self.client.v2.apps.get_instances(self['metadata']['guid']) def start(self) -> 'Application': @@ -23,6 +23,9 @@ def start(self) -> 'Application': def stop(self) -> 'Application': return self.client.v2.apps.stop(self['metadata']['guid']) + def restart_instance(self, instance_id: int): + return self.client.v2.apps.restart_instance(self['metadata']['guid'], instance_id) + def stats(self) -> Dict[str, JsonObject]: return self.client.v2.apps.get_stats(self['metadata']['guid']) @@ -97,6 +100,9 @@ def stop(self, application_guid: str, check_time: Optional[float] = 0.5, timeout self._wait_for_instances_in_state(application_guid, 0, 'STOPPED', check_time, timeout) return result + def restart_instance(self, application_guid: str, instance_id: int): + self._delete("%s%s/%s/instances/%s" % (self.target_endpoint, self.entity_uri, application_guid, instance_id)) + def restage(self, application_guid: str) -> Application: return self._post("%s%s/%s/restage" % (self.target_endpoint, self.entity_uri, application_guid)) diff --git a/test/v2/test_apps.py b/test/v2/test_apps.py index 9a30a99..5b9f6fc 100644 --- a/test/v2/test_apps.py +++ b/test/v2/test_apps.py @@ -172,6 +172,13 @@ def test_stop(self): self.client.get.assert_called_with('%s/v2/apps/app_id/instances' % TARGET_ENDPOINT) self.assertIsNotNone(application) + def test_restart_instance(self): + self.client.delete.return_value = mock_response( + '/v2/apps/app_id/instances/666', + HTTPStatus.NO_CONTENT, None) + self.client.v2.apps.restart_instance('app_id', 666) + self.client.delete.assert_called_with(self.client.delete.return_value.url) + def test_create(self): self.client.post.return_value = mock_response( '/v2/apps', @@ -287,3 +294,14 @@ def test_main_restage_app(self): 'v2', 'apps', 'POST_{id}_restage_response.json') main.main() self.client.post.assert_called_with(self.client.post.return_value.url, json=None) + + @patch.object(sys, 'argv', ['main', 'restart_instance', '906775ea-622e-4bc7-af5d-9aab3b652f81', '666']) + def test_main_restart_app_instance(self): + with patch('cloudfoundry_client.main.main.build_client_from_configuration', + new=lambda: self.client): + self.client.delete.return_value = mock_response( + '/v2/apps/906775ea-622e-4bc7-af5d-9aab3b652f81/instances/666', + HTTPStatus.NO_CONTENT, + None) + main.main() + self.client.delete.assert_called_with(self.client.delete.return_value.url) From 1264ae486e95de17cf3964644e55b9cc17f2e25e Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Mon, 9 Mar 2020 22:33:28 +0100 Subject: [PATCH 016/264] Implement Isoleation Segments V3 Close #72 --- main/cloudfoundry_client/client.py | 2 + main/cloudfoundry_client/v3/entities.py | 9 +- .../v3/isolation_segments.py | 53 +++++++ .../v3/isolation_segments/GET_response.json | 106 +++++++++++++ ..._relationships_organizations_response.json | 18 +++ ...ET_{id}_relationships_spaces_response.json | 15 ++ .../isolation_segments/GET_{id}_response.json | 19 +++ .../PATCH_{id}_response.json | 18 +++ .../v3/isolation_segments/POST_response.json | 18 +++ ..._relationships_organizations_response.json | 18 +++ test/v3/test_isolation_segments.py | 140 ++++++++++++++++++ 11 files changed, 412 insertions(+), 4 deletions(-) create mode 100644 main/cloudfoundry_client/v3/isolation_segments.py create mode 100644 test/fixtures/v3/isolation_segments/GET_response.json create mode 100644 test/fixtures/v3/isolation_segments/GET_{id}_relationships_organizations_response.json create mode 100644 test/fixtures/v3/isolation_segments/GET_{id}_relationships_spaces_response.json create mode 100644 test/fixtures/v3/isolation_segments/GET_{id}_response.json create mode 100644 test/fixtures/v3/isolation_segments/PATCH_{id}_response.json create mode 100644 test/fixtures/v3/isolation_segments/POST_response.json create mode 100644 test/fixtures/v3/isolation_segments/POST_{id}_relationships_organizations_response.json create mode 100644 test/v3/test_isolation_segments.py diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index ccb9bb8..c57621c 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -27,6 +27,7 @@ from cloudfoundry_client.v3.domains import DomainManager from cloudfoundry_client.v3.entities import EntityManager as EntityManagerV3 from cloudfoundry_client.v3.feature_flags import FeatureFlagManager +from cloudfoundry_client.v3.isolation_segments import IsolationSegmentManager from cloudfoundry_client.v3.organizations import OrganizationManager from cloudfoundry_client.v3.tasks import TaskManager @@ -81,6 +82,7 @@ def __init__(self, target_endpoint: str, credential_manager: 'CloudFoundryClient self.buildpacks = BuildpackManagerV3(target_endpoint, credential_manager) self.domains = DomainManager(target_endpoint, credential_manager) self.feature_flags = FeatureFlagManager(target_endpoint, credential_manager) + self.isolation_segments = IsolationSegmentManager(target_endpoint, credential_manager) self.spaces = EntityManagerV3(target_endpoint, credential_manager, '/v3/spaces') self.organizations = OrganizationManager(target_endpoint, credential_manager) self.service_instances = EntityManagerV3(target_endpoint, credential_manager, '/v3/service_instances') diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index 60fc20b..8e359f8 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -71,11 +71,12 @@ def __init__(self, guid: Optional[str]): class ToManyRelationship(JsonObject): @staticmethod def from_json_object(to_many_relations: JsonObject): - return ToManyRelationship( - *[relation['guid'] for relation in to_many_relations['data']]) + guids = [relation['guid'] for relation in to_many_relations['data']] + to_many_relations.pop('data') + return ToManyRelationship(*guids, **to_many_relations) - def __init__(self, *guids: str): - super(ToManyRelationship, self).__init__(data=[Relationship(guid) for guid in guids]) + def __init__(self, *guids: str, **kwargs): + super(ToManyRelationship, self).__init__(data=[Relationship(guid) for guid in guids], **kwargs) self.guids = list(guids) diff --git a/main/cloudfoundry_client/v3/isolation_segments.py b/main/cloudfoundry_client/v3/isolation_segments.py new file mode 100644 index 0000000..4769f6c --- /dev/null +++ b/main/cloudfoundry_client/v3/isolation_segments.py @@ -0,0 +1,53 @@ +from typing import Optional + +from cloudfoundry_client.v3.entities import EntityManager, Entity, ToManyRelationship + + +class IsolationSegmentManager(EntityManager): + def __init__(self, target_endpoint: str, client: 'CloudfoundryClient'): + super(IsolationSegmentManager, self).__init__(target_endpoint, client, '/v3/isolation_segments') + + def create(self, name: str, + meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Entity: + data = { + 'name': name, + 'metadata': { + 'labels': meta_labels, + 'annotations': meta_annotations + } + } + return super(IsolationSegmentManager, self)._create(data) + + def update(self, isolation_segment_guid: str, name: str, + meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Entity: + data = { + 'name': name, + 'metadata': { + 'labels': meta_labels, + 'annotations': meta_annotations + } + } + return super(IsolationSegmentManager, self)._update(isolation_segment_guid, data) + + def entitle_organizations(self, isolation_segment_guid: str, *org_guids: str) -> ToManyRelationship: + data = ToManyRelationship(*org_guids) + return ToManyRelationship.from_json_object(super(IsolationSegmentManager, self)._post( + '%s%s/%s/relationships/organizations' % (self.target_endpoint, self.entity_uri, isolation_segment_guid), + data=data)) + + def list_entitled_organizations(self, isolation_segment_guid: str, ) -> ToManyRelationship: + return ToManyRelationship.from_json_object(super(IsolationSegmentManager, self)._get( + '%s%s/%s/relationships/organizations' % (self.target_endpoint, self.entity_uri, isolation_segment_guid))) + + def list_entitled_spaces(self, isolation_segment_guid: str, ) -> ToManyRelationship: + return ToManyRelationship.from_json_object(super(IsolationSegmentManager, self)._get( + '%s%s/%s/relationships/spaces' % (self.target_endpoint, self.entity_uri, isolation_segment_guid))) + + def revoke_organization(self, isolation_segment_guid: str, org_guid: str): + super(IsolationSegmentManager, self)._delete('%s%s/%s/relationships/organizations/%s' % (self.target_endpoint, + self.entity_uri, + isolation_segment_guid, + org_guid)) + + def remove(self, isolation_segment_guid: str): + super(IsolationSegmentManager, self)._remove(isolation_segment_guid) diff --git a/test/fixtures/v3/isolation_segments/GET_response.json b/test/fixtures/v3/isolation_segments/GET_response.json new file mode 100644 index 0000000..9e768b1 --- /dev/null +++ b/test/fixtures/v3/isolation_segments/GET_response.json @@ -0,0 +1,106 @@ +{ + "pagination": { + "total_results": 5, + "total_pages": 1, + "first": { + "href": "https://api.example.org/v3/isolation_segments?page=1&per_page=5" + }, + "last": { + "href": "https://api.example.org/v3/isolation_segments?page=3&per_page=5" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "b19f6525-cbd3-4155-b156-dc0c2a431b4c", + "name": "an_isolation_segment", + "created_at": "2016-10-19T20:25:04Z", + "updated_at": "2016-11-08T16:41:26Z", + "links": { + "self": { + "href": "https://api.example.org/v3/isolation_segments/b19f6525-cbd3-4155-b156-dc0c2a431b4c" + }, + "organizations": { + "href": "https://api.example.org/v3/isolation_segments/b19f6525-cbd3-4155-b156-dc0c2a431b4c/organizations" + } + }, + "metadata": { + "annotations": {}, + "labels": {} + } + }, + { + "guid": "68d54d31-9b3a-463b-ba94-e8e4c32edbac", + "name": "an_isolation_segment1", + "created_at": "2016-10-19T20:29:19Z", + "updated_at": "2016-11-08T16:41:26Z", + "links": { + "self": { + "href": "https://api.example.org/v3/isolation_segments/68d54d31-9b3a-463b-ba94-e8e4c32edbac" + }, + "organizations": { + "href": "https://api.example.org/v3/isolation_segments/68d54d31-9b3a-463b-ba94-e8e4c32edbac/organizations" + } + }, + "metadata": { + "annotations": {}, + "labels": {} + } + }, + { + "guid": "ecdc67c3-a71e-43ff-bddf-048930b8cd03", + "name": "an_isolation_segment2", + "created_at": "2016-10-19T20:29:22Z", + "updated_at": "2016-11-08T16:41:26Z", + "links": { + "self": { + "href": "https://api.example.org/v3/isolation_segments/ecdc67c3-a71e-43ff-bddf-048930b8cd03" + }, + "organizations": { + "href": "https://api.example.org/v3/isolation_segments/ecdc67c3-a71e-43ff-bddf-048930b8cd03/organizations" + } + }, + "metadata": { + "annotations": {}, + "labels": {} + } + }, + { + "guid": "424c89e4-4353-46b7-9bf4-f90bd9bacac0", + "name": "an_isolation_segment3", + "created_at": "2016-10-19T20:29:27Z", + "updated_at": "2016-11-08T16:41:26Z", + "links": { + "self": { + "href": "https://api.example.org/v3/isolation_segments/424c89e4-4353-46b7-9bf4-f90bd9bacac0" + }, + "organizations": { + "href": "https://api.example.org/v3/isolation_segments/424c89e4-4353-46b7-9bf4-f90bd9bacac0/organizations" + } + }, + "metadata": { + "annotations": {}, + "labels": {} + } + }, + { + "guid": "0a79fcec-a648-4eb8-a6c3-2b5be39047c7", + "name": "an_isolation_segment4", + "created_at": "2016-10-19T20:29:33Z", + "updated_at": "2016-11-08T16:41:26Z", + "links": { + "self": { + "href": "https://api.example.org/v3/isolation_segments/0a79fcec-a648-4eb8-a6c3-2b5be39047c7" + }, + "organizations": { + "href": "https://api.example.org/v3/isolation_segments/0a79fcec-a648-4eb8-a6c3-2b5be39047c7/organizations" + } + }, + "metadata": { + "annotations": {}, + "labels": {} + } + } + ] +} diff --git a/test/fixtures/v3/isolation_segments/GET_{id}_relationships_organizations_response.json b/test/fixtures/v3/isolation_segments/GET_{id}_relationships_organizations_response.json new file mode 100644 index 0000000..c9674d8 --- /dev/null +++ b/test/fixtures/v3/isolation_segments/GET_{id}_relationships_organizations_response.json @@ -0,0 +1,18 @@ +{ + "data": [ + { + "guid": "68d54d31-9b3a-463b-ba94-e8e4c32edbac" + }, + { + "guid": "b19f6525-cbd3-4155-b156-dc0c2a431b4c" + } + ], + "links": { + "self": { + "href": "https://api.example.org/v3/isolation_segments/bdeg4371-cbd3-4155-b156-dc0c2a431b4c/relationships/organizations" + }, + "related": { + "href": "https://api.example.org/v3/isolation_segments/bdeg4371-cbd3-4155-b156-dc0c2a431b4c/organizations" + } + } +} diff --git a/test/fixtures/v3/isolation_segments/GET_{id}_relationships_spaces_response.json b/test/fixtures/v3/isolation_segments/GET_{id}_relationships_spaces_response.json new file mode 100644 index 0000000..f6bb561 --- /dev/null +++ b/test/fixtures/v3/isolation_segments/GET_{id}_relationships_spaces_response.json @@ -0,0 +1,15 @@ +{ + "data": [ + { + "guid": "885735b5-aea4-4cf5-8e44-961af0e41920" + }, + { + "guid": "d4c91047-7b29-4fda-b7f9-04033e5c9c9f" + } + ], + "links": { + "self": { + "href": "https://api.example.org/v3/isolation_segments/bdeg4371-cbd3-4155-b156-dc0c2a431b4c/relationships/spaces" + } + } +} \ No newline at end of file diff --git a/test/fixtures/v3/isolation_segments/GET_{id}_response.json b/test/fixtures/v3/isolation_segments/GET_{id}_response.json new file mode 100644 index 0000000..2aa2be3 --- /dev/null +++ b/test/fixtures/v3/isolation_segments/GET_{id}_response.json @@ -0,0 +1,19 @@ +{ + "guid": "b19f6525-cbd3-4155-b156-dc0c2a431b4c", + "name": "an_isolation_segment", + "created_at": "2016-10-19T20:25:04Z", + "updated_at": "2016-11-08T16:41:26Z", + "links": { + "self": { + "href": "https://api.example.org/v3/isolation_segments/b19f6525-cbd3-4155-b156-dc0c2a431b4c" + }, + "organizations": { + "href": "https://api.example.org/v3/isolation_segments/b19f6525-cbd3-4155-b156-dc0c2a431b4c/organizations" + } + }, + "metadata": { + "annotations": {}, + "labels": {} + } +} + diff --git a/test/fixtures/v3/isolation_segments/PATCH_{id}_response.json b/test/fixtures/v3/isolation_segments/PATCH_{id}_response.json new file mode 100644 index 0000000..1a1a930 --- /dev/null +++ b/test/fixtures/v3/isolation_segments/PATCH_{id}_response.json @@ -0,0 +1,18 @@ +{ + "guid": "b19f6525-cbd3-4155-b156-dc0c2a431b4c", + "name": "my_isolation_segment", + "created_at": "2016-10-19T20:25:04Z", + "updated_at": "2016-11-08T16:41:26Z", + "links": { + "self": { + "href": "https://api.example.org/v3/isolation_segments/b19f6525-cbd3-4155-b156-dc0c2a431b4c" + }, + "organizations": { + "href": "https://api.example.org/v3/isolation_segments/b19f6525-cbd3-4155-b156-dc0c2a431b4c/organizations" + } + }, + "metadata": { + "annotations": {}, + "labels": {} + } +} diff --git a/test/fixtures/v3/isolation_segments/POST_response.json b/test/fixtures/v3/isolation_segments/POST_response.json new file mode 100644 index 0000000..2bc5b59 --- /dev/null +++ b/test/fixtures/v3/isolation_segments/POST_response.json @@ -0,0 +1,18 @@ +{ + "guid": "b19f6525-cbd3-4155-b156-dc0c2a431b4c", + "name": "an_isolation_segment", + "created_at": "2016-10-19T20:25:04Z", + "updated_at": "2016-11-08T16:41:26Z", + "links": { + "self": { + "href": "https://api.example.org/v3/isolation_segments/b19f6525-cbd3-4155-b156-dc0c2a431b4c" + }, + "organizations": { + "href": "https://api.example.org/v3/isolation_segments/b19f6525-cbd3-4155-b156-dc0c2a431b4c/organizations" + } + }, + "metadata": { + "annotations": {}, + "labels": {} + } +} \ No newline at end of file diff --git a/test/fixtures/v3/isolation_segments/POST_{id}_relationships_organizations_response.json b/test/fixtures/v3/isolation_segments/POST_{id}_relationships_organizations_response.json new file mode 100644 index 0000000..4bc1c91 --- /dev/null +++ b/test/fixtures/v3/isolation_segments/POST_{id}_relationships_organizations_response.json @@ -0,0 +1,18 @@ +{ + "data": [ + { + "guid": "68d54d31-9b3a-463b-ba94-e8e4c32edbac" + }, + { + "guid": "b19f6525-cbd3-4155-b156-dc0c2a431b4c" + } + ], + "links": { + "self": { + "href": "https://api.example.org/v3/isolation_segments/bdeg4371-cbd3-4155-b156-dc0c2a431b4c/relationships/organizations" + }, + "related": { + "href": "https://api.example.org/v3/isolation_segments/bdeg4371-cbd3-4155-b156-dc0c2a431b4c/organizations" + } + } +} \ No newline at end of file diff --git a/test/v3/test_isolation_segments.py b/test/v3/test_isolation_segments.py new file mode 100644 index 0000000..035aec9 --- /dev/null +++ b/test/v3/test_isolation_segments.py @@ -0,0 +1,140 @@ +import unittest +from http import HTTPStatus + +from abstract_test_case import AbstractTestCase +from cloudfoundry_client.v3.entities import Entity, ToManyRelationship +from fake_requests import mock_response + + +class TestIsolationSegments(unittest.TestCase, AbstractTestCase): + @classmethod + def setUpClass(cls): + cls.mock_client_class() + + def setUp(self): + self.build_client() + + def test_list(self): + self.client.get.return_value = mock_response('/v3/isolation_segments', + HTTPStatus.OK, + None, + 'v3', 'isolation_segments', 'GET_response.json') + all_isolation_segments = [isolation_segment for isolation_segment in self.client.v3.isolation_segments.list()] + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual(5, len(all_isolation_segments)) + self.assertEqual(all_isolation_segments[0]['name'], "an_isolation_segment") + self.assertIsInstance(all_isolation_segments[0], Entity) + for isolation_segment in all_isolation_segments: + self.assertIsInstance(isolation_segment, Entity) + + def test_get(self): + self.client.get.return_value = mock_response( + '/v3/isolation_segments/isolation_segment_id', + HTTPStatus.OK, + None, + 'v3', 'isolation_segments', 'GET_{id}_response.json') + result = self.client.v3.isolation_segments.get('isolation_segment_id') + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertIsNotNone(result) + self.assertIsInstance(result, Entity) + + def test_update(self): + self.client.patch.return_value = mock_response( + '/v3/isolation_segments/isolation_segment_id', + HTTPStatus.OK, + None, + 'v3', 'isolation_segments', 'PATCH_{id}_response.json') + result = self.client.v3.isolation_segments.update('isolation_segment_id', + 'new-name', + meta_labels=dict(key="value")) + self.client.patch.assert_called_with(self.client.patch.return_value.url, + json={ + 'name': 'new-name', + 'metadata': { + 'labels': {'key': 'value'}, + 'annotations': None + } + }) + self.assertIsNotNone(result) + self.assertIsInstance(result, Entity) + + def test_create(self): + self.client.post.return_value = mock_response( + '/v3/isolation_segments', + HTTPStatus.OK, + None, + 'v3', 'isolation_segments', 'POST_response.json') + result = self.client.v3.isolation_segments.create('isolation_segment_id', + meta_labels=dict(key_label="value_label"), + meta_annotations=dict(key_annotation="value_annotation")) + self.client.post.assert_called_with(self.client.post.return_value.url, + files=None, + json={'name': 'isolation_segment_id', + 'metadata': + { + 'labels': {'key_label': 'value_label'}, + 'annotations': {'key_annotation': 'value_annotation'} + } + }) + self.assertIsNotNone(result) + self.assertIsInstance(result, Entity) + + def test_remove(self): + self.client.delete.return_value = mock_response( + '/v3/isolation_segments/isolation_segment_id', + HTTPStatus.NO_CONTENT, + None) + self.client.v3.isolation_segments.remove('isolation_segment_id') + self.client.delete.assert_called_with(self.client.delete.return_value.url) + + def test_entitle_organizations(self): + self.client.post.return_value = mock_response( + '/v3/isolation_segments/isolation_segment_id/relationships/organizations', + HTTPStatus.OK, + None, + 'v3', 'isolation_segments', 'POST_{id}_relationships_organizations_response.json') + result = self.client.v3.isolation_segments.entitle_organizations('isolation_segment_id', 'org_id_1', 'org_id_2') + self.client.post.assert_called_with(self.client.post.return_value.url, + files=None, + json={ + 'data': [ + {'guid': 'org_id_1'}, + {'guid': 'org_id_2'} + ] + }) + self.assertIsInstance(result, ToManyRelationship) + self.assertEqual(2, len(result.guids)) + self.assertIsNotNone(result['links']) + + def test_revoke_organization(self): + self.client.delete.return_value = mock_response( + '/v3/isolation_segments/isolation_segment_id/relationships/organizations/org_id', + HTTPStatus.NO_CONTENT, + None) + self.client.v3.isolation_segments.revoke_organization('isolation_segment_id', 'org_id') + self.client.delete.assert_called_with(self.client.delete.return_value.url) + + def test_list_entitled_organizations(self): + self.client.get.return_value = mock_response( + '/v3/isolation_segments/isolation_segment_id/relationships/organizations', + HTTPStatus.OK, + None, + 'v3', 'isolation_segments', 'GET_{id}_relationships_organizations_response.json') + result = self.client.v3.isolation_segments.list_entitled_organizations('isolation_segment_id') + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertIsInstance(result, ToManyRelationship) + self.assertEqual(2, len(result.guids)) + self.assertIsNotNone(result['links']) + + def test_list_entitled_spaces(self): + self.client.get.return_value = mock_response( + '/v3/isolation_segments/isolation_segment_id/relationships/spaces', + HTTPStatus.OK, + None, + 'v3', 'isolation_segments', 'GET_{id}_relationships_spaces_response.json') + result = self.client.v3.isolation_segments.list_entitled_spaces('isolation_segment_id') + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertIsInstance(result, ToManyRelationship) + self.assertEqual(2, len(result.guids)) + self.assertIsNotNone(result['links']) + From a5ed7f9dd06a520f60e9c3f2076491e9ac6f1ca9 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Wed, 11 Mar 2020 22:17:57 +0100 Subject: [PATCH 017/264] Implement Space Manager V3 See #72 --- main/cloudfoundry_client/client.py | 3 +- main/cloudfoundry_client/v3/domains.py | 8 +-- main/cloudfoundry_client/v3/entities.py | 18 +++--- main/cloudfoundry_client/v3/spaces.py | 28 +++++++++ ...ationships_isolation_segment_response.json | 14 +++++ test/fixtures/v3/spaces/POST_response.json | 28 +++++++++ ...ationships_isolation_segment_response.json | 5 ++ test/v3/test_spaces.py | 59 ++++++++++++++++++- 8 files changed, 149 insertions(+), 14 deletions(-) create mode 100644 main/cloudfoundry_client/v3/spaces.py create mode 100644 test/fixtures/v3/spaces/GET_{id}_relationships_isolation_segment_response.json create mode 100644 test/fixtures/v3/spaces/POST_response.json create mode 100644 test/fixtures/v3/spaces/POST_{id}_relationships_isolation_segment_response.json diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index c57621c..e3e87e5 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -29,6 +29,7 @@ from cloudfoundry_client.v3.feature_flags import FeatureFlagManager from cloudfoundry_client.v3.isolation_segments import IsolationSegmentManager from cloudfoundry_client.v3.organizations import OrganizationManager +from cloudfoundry_client.v3.spaces import SpaceManager from cloudfoundry_client.v3.tasks import TaskManager _logger = logging.getLogger(__name__) @@ -83,7 +84,7 @@ def __init__(self, target_endpoint: str, credential_manager: 'CloudFoundryClient self.domains = DomainManager(target_endpoint, credential_manager) self.feature_flags = FeatureFlagManager(target_endpoint, credential_manager) self.isolation_segments = IsolationSegmentManager(target_endpoint, credential_manager) - self.spaces = EntityManagerV3(target_endpoint, credential_manager, '/v3/spaces') + self.spaces = SpaceManager(target_endpoint, credential_manager) self.organizations = OrganizationManager(target_endpoint, credential_manager) self.service_instances = EntityManagerV3(target_endpoint, credential_manager, '/v3/service_instances') self.tasks = TaskManager(target_endpoint, credential_manager) diff --git a/main/cloudfoundry_client/v3/domains.py b/main/cloudfoundry_client/v3/domains.py index 60e8da1..3716dec 100644 --- a/main/cloudfoundry_client/v3/domains.py +++ b/main/cloudfoundry_client/v3/domains.py @@ -9,7 +9,7 @@ def __init__(self, target_endpoint: str, entity_manager: 'EntityManager', **kwar super(Domain, self).__init__(target_endpoint, entity_manager, **kwargs) relationships = self['relationships'] if 'organization' in relationships: - self['relationships']['organization'] = ToOneRelationship.from_json_object(relationships['organization'].get('data')) + self['relationships']['organization'] = ToOneRelationship.from_json_object(relationships['organization']) if 'shared_organizations' in relationships: self['relationships']['shared_organizations'] \ = ToManyRelationship.from_json_object(relationships['shared_organizations']) @@ -22,7 +22,7 @@ def __init__(self, target_endpoint: str, client: 'CloudfoundryClient'): def create(self, name: str, internal: Optional[bool] = False, organization: Optional[ToOneRelationship] = None, shared_organizations: Optional[ToManyRelationship] = None, - meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None): + meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Domain: data = { 'name': name, 'internal': internal, @@ -40,7 +40,7 @@ def list_domains_for_org(self, org_guid: str, **kwargs) -> PaginateEntities: return self._list(uri, **kwargs) def update(self, domain_guid: str, - meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Entity: + meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Domain: data = { 'metadata': { 'labels': meta_labels, @@ -58,7 +58,7 @@ def __create_shared_domain_url(self, domain_guid: str) -> str: ''.format(endpoint=self.target_endpoint, entity=self.entity_uri, domain=domain_guid) - def share_domain(self, domain_guid: str, organization_guids: ToManyRelationship): + def share_domain(self, domain_guid: str, organization_guids: ToManyRelationship) -> ToManyRelationship: url = self.__create_shared_domain_url(domain_guid) return ToManyRelationship.from_json_object(super(DomainManager, self)._post(url, data=organization_guids)) diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index 8e359f8..7f8ef13 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -60,8 +60,12 @@ def __init__(self, guid: Optional[str]): class ToOneRelationship(JsonObject): @staticmethod def from_json_object(to_one_relationship: JsonObject): - return ToOneRelationship(None) if to_one_relationship is None \ - else ToOneRelationship(to_one_relationship['guid']) + if to_one_relationship is None: + return ToOneRelationship(None) + data = to_one_relationship.pop('data', None) + result = ToOneRelationship(None if data is None else data['guid']) + result.update(to_one_relationship) + return result def __init__(self, guid: Optional[str]): super(ToOneRelationship, self).__init__(data=Relationship(guid)) @@ -71,12 +75,12 @@ def __init__(self, guid: Optional[str]): class ToManyRelationship(JsonObject): @staticmethod def from_json_object(to_many_relations: JsonObject): - guids = [relation['guid'] for relation in to_many_relations['data']] - to_many_relations.pop('data') - return ToManyRelationship(*guids, **to_many_relations) + result = ToManyRelationship(*[relation['guid'] for relation in to_many_relations.pop('data')]) + result.update(to_many_relations) + return result - def __init__(self, *guids: str, **kwargs): - super(ToManyRelationship, self).__init__(data=[Relationship(guid) for guid in guids], **kwargs) + def __init__(self, *guids: str): + super(ToManyRelationship, self).__init__(data=[Relationship(guid) for guid in guids]) self.guids = list(guids) diff --git a/main/cloudfoundry_client/v3/spaces.py b/main/cloudfoundry_client/v3/spaces.py new file mode 100644 index 0000000..1ca7267 --- /dev/null +++ b/main/cloudfoundry_client/v3/spaces.py @@ -0,0 +1,28 @@ +from typing import Optional + +from cloudfoundry_client.v3.entities import EntityManager, ToOneRelationship, Entity + + +class SpaceManager(EntityManager): + def __init__(self, target_endpoint: str, client: 'CloudfoundryClient'): + super(SpaceManager, self).__init__(target_endpoint, client, '/v3/spaces') + + def create(self, name: str, org_guid: str) -> Entity: + return super(SpaceManager, self)._create(dict(name=name, + relationships=dict(organization=ToOneRelationship(org_guid)))) + + def update(self, space_guid: str, name: str) -> Entity: + return super(SpaceManager, self)._update(space_guid, dict(name=name)) + + def get_assigned_isolation_segment(self, space_guid: str) -> ToOneRelationship: + return ToOneRelationship.from_json_object(super(SpaceManager, self)._get( + '%s%s/%s/relationships/isolation_segment' % (self.target_endpoint, self.entity_uri, space_guid))) + + def assign_isolation_segment(self, space_guid: str, isolation_segment_guid: Optional[str]) -> ToOneRelationship: + return ToOneRelationship.from_json_object(super(SpaceManager, self)._patch( + '%s%s/%s/relationships/isolation_segment' % (self.target_endpoint, self.entity_uri, space_guid), + dict(data=None) if isolation_segment_guid is None else ToOneRelationship(isolation_segment_guid) + )) + + def remove(self, space_guid: str): + super(SpaceManager, self)._remove(space_guid) diff --git a/test/fixtures/v3/spaces/GET_{id}_relationships_isolation_segment_response.json b/test/fixtures/v3/spaces/GET_{id}_relationships_isolation_segment_response.json new file mode 100644 index 0000000..eb2873b --- /dev/null +++ b/test/fixtures/v3/spaces/GET_{id}_relationships_isolation_segment_response.json @@ -0,0 +1,14 @@ +{ + "data": { + "guid": "e4c91047-3b29-4fda-b7f9-04033e5a9c9f" + }, + "links": { + "self": { + "href": "https://api.example.org/v3/spaces/885735b5-aea4-4cf5-8e44-961af0e41920/relationships/isolation_segment" + }, + "related": { + "href": "https://api.example.org/v3/isolation_segments/e4c91047-3b29-4fda-b7f9-04033e5a9c9f" + } + } +} + diff --git a/test/fixtures/v3/spaces/POST_response.json b/test/fixtures/v3/spaces/POST_response.json new file mode 100644 index 0000000..430279b --- /dev/null +++ b/test/fixtures/v3/spaces/POST_response.json @@ -0,0 +1,28 @@ +{ + "guid": "885735b5-aea4-4cf5-8e44-961af0e41920", + "created_at": "2017-02-01T01:33:58Z", + "updated_at": "2017-02-01T01:33:58Z", + "name": "my-space", + "relationships": { + "organization": { + "data": { + "guid": "e00705b9-7b42-4561-ae97-2520399d2133" + } + }, + "quota": { + "data": null + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/spaces/885735b5-aea4-4cf5-8e44-961af0e41920" + }, + "organization": { + "href": "https://api.example.org/v3/organizations/e00705b9-7b42-4561-ae97-2520399d2133" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } +} \ No newline at end of file diff --git a/test/fixtures/v3/spaces/POST_{id}_relationships_isolation_segment_response.json b/test/fixtures/v3/spaces/POST_{id}_relationships_isolation_segment_response.json new file mode 100644 index 0000000..94af7fc --- /dev/null +++ b/test/fixtures/v3/spaces/POST_{id}_relationships_isolation_segment_response.json @@ -0,0 +1,5 @@ +{ + "data": { + "guid": "iso-seg-guid" + } +} \ No newline at end of file diff --git a/test/v3/test_spaces.py b/test/v3/test_spaces.py index 3d66e55..712481e 100644 --- a/test/v3/test_spaces.py +++ b/test/v3/test_spaces.py @@ -3,7 +3,7 @@ from unittest.mock import call from abstract_test_case import AbstractTestCase -from cloudfoundry_client.v3.entities import Entity +from cloudfoundry_client.v3.entities import Entity, ToOneRelationship from fake_requests import mock_response @@ -15,6 +15,26 @@ def setUpClass(cls): def setUp(self): self.build_client() + def test_create(self): + self.client.post.return_value = mock_response( + '/v3/spaces', + HTTPStatus.OK, + None, + 'v3', 'spaces', 'POST_response.json') + result = self.client.v3.spaces.create('space-name', 'organization-guid') + self.client.post.assert_called_with(self.client.post.return_value.url, + files=None, + json={'name': 'space-name', + 'relationships': { + 'organization': { + 'data': { + 'guid': 'organization-guid' + } + } + }}) + self.assertIsNotNone(result) + self.assertIsInstance(result, Entity) + def test_list(self): self.client.get.return_value = mock_response('/v3/spaces', HTTPStatus.OK, @@ -36,7 +56,7 @@ def test_get(self): self.assertEqual("my-space", space['name']) self.assertIsInstance(space, Entity) - def test_get_then_space(self): + def test_get_then_organization(self): get_space = mock_response('/v3/spaces/space_id', HTTPStatus.OK, None, 'v3', 'spaces', 'GET_{id}_response.json') get_organization = mock_response('/v3/organizations/e00705b9-7b42-4561-ae97-2520399d2133', HTTPStatus.OK, None, @@ -51,3 +71,38 @@ def test_get_then_space(self): any_order=False) self.assertEqual("my-organization", organization['name']) + def test_get_assigned_isolation_segment(self): + self.client.get.return_value = mock_response('/v3/spaces/space_id/relationships/isolation_segment', + HTTPStatus.OK, + None, + 'v3', 'spaces', + 'GET_{id}_relationships_isolation_segment_response.json') + + result = self.client.v3.spaces.get_assigned_isolation_segment('space_id') + + self.assertIsInstance(result, ToOneRelationship) + self.assertEqual('e4c91047-3b29-4fda-b7f9-04033e5a9c9f', result.guid) + + def test_assign_isolation_segment(self): + self.client.patch.return_value = mock_response('/v3/spaces/space_id/relationships/isolation_segment', + HTTPStatus.OK, + None, + 'v3', 'spaces', + 'POST_{id}_relationships_isolation_segment_response.json') + result = self.client.v3.spaces.assign_isolation_segment('space_id', 'iso-seg-guid') + self.client.patch.assert_called_with(self.client.patch.return_value.url, + json={ + "data": { + "guid": "iso-seg-guid" + } + }) + self.assertIsInstance(result, ToOneRelationship) + self.assertEqual('iso-seg-guid', result.guid) + + def test_remove(self): + self.client.delete.return_value = mock_response( + '/v3/spaces/space_id', + HTTPStatus.NO_CONTENT, + None) + self.client.v3.spaces.remove('space_id') + self.client.delete.assert_called_with(self.client.delete.return_value.url) From 7a3a557419a51851172d31feb97dafcb37513ad5 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Thu, 12 Mar 2020 13:48:53 +0100 Subject: [PATCH 018/264] Improve isolation segment implementation for organization --- main/cloudfoundry_client/v3/organizations.py | 23 ++++++------- ...s_default_isolation_segment_response.json} | 0 ...s_default_isolation_segment_response.json} | 0 test/v3/test_organizations.py | 32 ++++++++++++------- 4 files changed, 29 insertions(+), 26 deletions(-) rename test/fixtures/v3/organizations/{GET_{id}_default_isolation_segment_response.json => GET_{id}_relationships_default_isolation_segment_response.json} (100%) rename test/fixtures/v3/organizations/{PATCH_{id}_assign_default_isolation_segment_response.json => PATCH_{id}_relationships_default_isolation_segment_response.json} (100%) diff --git a/main/cloudfoundry_client/v3/organizations.py b/main/cloudfoundry_client/v3/organizations.py index a8bee7b..93061ec 100644 --- a/main/cloudfoundry_client/v3/organizations.py +++ b/main/cloudfoundry_client/v3/organizations.py @@ -1,6 +1,6 @@ from typing import Optional -from cloudfoundry_client.v3.entities import EntityManager, Entity +from cloudfoundry_client.v3.entities import EntityManager, Entity, ToOneRelationship class OrganizationManager(EntityManager): @@ -37,19 +37,14 @@ def remove(self, guid: str): super(OrganizationManager, self)._remove(guid) def assign_default_isolation_segment(self, org_guid: str, iso_seg_guid: str) -> Entity: - url = '{endpoint}{entity}/{guid}/relationships/default_isolation_segment' \ - ''.format(endpoint=self.target_endpoint, - entity=self.entity_uri, - guid=org_guid) - data = { - "data": { - "guid": iso_seg_guid - } - } - return super(OrganizationManager, self)._patch(url, data) - - def get_default_isolation_segment(self, guid: str) -> Entity: - return super(OrganizationManager, self).get(guid, 'relationships', 'default_isolation_segment') + return ToOneRelationship.from_json_object( + super(OrganizationManager, self)._patch( + '%s%s/%s/relationships/default_isolation_segment' % (self.target_endpoint, self.entity_uri, org_guid), + data=ToOneRelationship(iso_seg_guid))) + + def get_default_isolation_segment(self, guid: str) -> ToOneRelationship: + return ToOneRelationship.from_json_object( + super(OrganizationManager, self).get(guid, 'relationships', 'default_isolation_segment')) def get_default_domain(self, guid: str) -> Entity: return super(OrganizationManager, self).get(guid, 'domains', 'default') diff --git a/test/fixtures/v3/organizations/GET_{id}_default_isolation_segment_response.json b/test/fixtures/v3/organizations/GET_{id}_relationships_default_isolation_segment_response.json similarity index 100% rename from test/fixtures/v3/organizations/GET_{id}_default_isolation_segment_response.json rename to test/fixtures/v3/organizations/GET_{id}_relationships_default_isolation_segment_response.json diff --git a/test/fixtures/v3/organizations/PATCH_{id}_assign_default_isolation_segment_response.json b/test/fixtures/v3/organizations/PATCH_{id}_relationships_default_isolation_segment_response.json similarity index 100% rename from test/fixtures/v3/organizations/PATCH_{id}_assign_default_isolation_segment_response.json rename to test/fixtures/v3/organizations/PATCH_{id}_relationships_default_isolation_segment_response.json diff --git a/test/v3/test_organizations.py b/test/v3/test_organizations.py index dfe8d4f..d3e5bfe 100644 --- a/test/v3/test_organizations.py +++ b/test/v3/test_organizations.py @@ -5,7 +5,7 @@ import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase -from cloudfoundry_client.v3.entities import Entity +from cloudfoundry_client.v3.entities import Entity, ToOneRelationship from fake_requests import mock_response @@ -53,7 +53,7 @@ def test_update(self): 'labels': None, 'annotations': None } - }) + }) self.assertIsNotNone(result) def test_create(self): @@ -72,7 +72,7 @@ def test_create(self): 'labels': None, 'annotations': None } - }) + }) self.assertIsNotNone(result) def test_remove(self): @@ -88,23 +88,31 @@ def test_assign_default_isolation_segment(self): '/v3/organizations/organization_id/relationships/default_isolation_segment', HTTPStatus.OK, None, - 'v3', 'organizations', 'PATCH_{id}_assign_default_isolation_segment_response.json') + 'v3', 'organizations', 'PATCH_{id}_relationships_default_isolation_segment_response.json') result = self.client.v3.organizations.assign_default_isolation_segment( 'organization_id', 'iso_seg_guid') self.client.patch.assert_called_with(self.client.patch.return_value.url, json={'data': { - 'guid': 'iso_seg_guid' - } + 'guid': 'iso_seg_guid' + } }) self.assertIsNotNone(result) + self.assertIsInstance(result, ToOneRelationship) + self.assertEqual(result.guid, "9d8e007c-ce52-4ea7-8a57-f2825d2c6b39") def test_get_default_isolation_segment(self): - self.client.get.return_value = mock_response('/v3/organizations/organization_id/relationships/default_isolation_segment', - HTTPStatus.OK, - None, - 'v3', 'organizations', 'GET_{id}_default_isolation_segment_response.json') - self.client.v3.organizations.get_default_isolation_segment('organization_id') + self.client.get.return_value = mock_response( + '/v3/organizations/organization_id/relationships/default_isolation_segment', + HTTPStatus.OK, + None, + 'v3', 'organizations', + 'GET_{id}_relationships_default_isolation_segment_response.json') + + result = self.client.v3.organizations.get_default_isolation_segment('organization_id') + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertIsInstance(result, ToOneRelationship) + self.assertEqual(result.guid, "9d8e007c-ce52-4ea7-8a57-f2825d2c6b39") def test_get_default_domain(self): self.client.get.return_value = mock_response('/v3/organizations/organization_id/domains/default', @@ -138,7 +146,7 @@ def test_main_list_organizations(self): @patch.object(sys, 'argv', ['main', 'get_organization', '24637893-3b77-489d-bb79-8466f0d88b52']) def test_main_get_organizations(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): + new=lambda: self.client): self.client.get.return_value = mock_response('/v3/organizations/24637893-3b77-489d-bb79-8466f0d88b52', HTTPStatus.OK, None, From a10bad34513ba1c8150e877a45070626dd5d1110 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Thu, 12 Mar 2020 19:24:18 +0100 Subject: [PATCH 019/264] Fix README --- README.rst | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/README.rst b/README.rst index ec89d6c..82c7ec8 100644 --- a/README.rst +++ b/README.rst @@ -14,7 +14,7 @@ Installing Supported versions ~~~~~~~~~~~~~~~~~~ -:warning: Starting version `1.11.0`, versions older that python `3.6.0` will not be supported anymore. This late version was released by the end 2016. +:warning: Starting version ``1.11.0``, versions older that python ``3.6.0`` will not be supported anymore. This late version was released by the end 2016. For those that are still using python 2.7, it won't be supported by the end of 2020 and all library shall stop supporting it. @@ -209,10 +209,10 @@ Available managers on API V3 are: The managers provide the same methods as the V2 managers. Networking ------- +---------- policy server -~~~~~~~~ +~~~~~~~~~~~~~ At the moment we have only the network policies implemented @@ -292,6 +292,3 @@ You can run tests by doing so. In the project directory: $ python -m unittest discover test # or even $ python setup.py test - - - From 5c628ea6ec5dc857a6b62e4f9e269c03b910822c Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Thu, 12 Mar 2020 19:25:15 +0100 Subject: [PATCH 020/264] Prepare 1.11 release --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 55c3a6c..a0a9265 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.10.0" +__version__ = "1.11.0" From ab2c5ffca3d5777940f6b7ada93aa73fa78e858b Mon Sep 17 00:00:00 2001 From: Alina Golovchenko Date: Fri, 13 Mar 2020 11:22:15 +0100 Subject: [PATCH 021/264] RLP gateway client --- README.rst | 15 +++++++ main/cloudfoundry_client/client.py | 17 ++++++++ .../rlpgateway/__init__.py | 0 main/cloudfoundry_client/rlpgateway/client.py | 39 +++++++++++++++++++ requirements.txt | 1 + 5 files changed, 72 insertions(+) create mode 100644 main/cloudfoundry_client/rlpgateway/__init__.py create mode 100644 main/cloudfoundry_client/rlpgateway/client.py diff --git a/README.rst b/README.rst index 82c7ec8..3e2022a 100644 --- a/README.rst +++ b/README.rst @@ -262,6 +262,21 @@ Logs can also be streamed using a websocket as follows: print(log) +Logs can also be streamed directly from RLP gateway: + +.. code-block:: python + + from cloudfoundry_client.client import CloudFoundryClient + target_endpoint = 'https://somewhere.org' + proxy = dict(http=os.environ.get('HTTP_PROXY', ''), https=os.environ.get('HTTPS_PROXY', '')) + rlp_client = CloudFoundryClient(target_endpoint, client_id='client_id', client_secret='client_secret', verify=False) + # init with client credentials + rlp_client.init_with_client_credentials() + + async for log in rlp_client.rlpgateway.stream_logs('app-guid'): + print(log) + + Command Line Interface ---------------------- diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index e3e87e5..3f00445 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -7,6 +7,7 @@ from requests import Response from cloudfoundry_client.doppler.client import DopplerClient +from cloudfoundry_client.rlpgateway.client import RLPGatewayClient from cloudfoundry_client.networking.v1.external.policies import PolicyManager from cloudfoundry_client.errors import InvalidStatusCode from cloudfoundry_client.v2.apps import AppManager as AppManagerV2 @@ -41,6 +42,8 @@ def __init__(self, api_version: str, authorization_endpoint: str, api_endpoint: self.authorization_endpoint = authorization_endpoint self.api_endpoint = api_endpoint self.doppler_endpoint = doppler_endpoint + domain = '.'.join(api_endpoint.split('.')[1:]) + self.rlp_gateway_endpoint = f'https://log-stream.{domain}' class NetworkingV1External(object): @@ -126,6 +129,12 @@ def __init__(self, target_endpoint: str, client_id: str = 'cf', client_secret: s 'http' if info.doppler_endpoint.startswith('ws://') else 'https'], self.service_information.verify, self) if info.doppler_endpoint is not None else None + self._rlpgateway = RLPGatewayClient(info.rlp_gateway_endpoint, + self.proxies[ + 'http' if info.rlp_gateway_endpoint.startswith('ws://') else 'https'], + self.service_information.verify, + self, + ) if info.rlp_gateway_endpoint is not None else None self.networking_v1_external = NetworkingV1External(target_endpoint_trimmed, self) self.info = info @@ -137,6 +146,14 @@ def doppler(self) -> DopplerClient: return self._doppler + @property + def rlpgateway(self): + if self._rlpgateway is None: + raise NotImplementedError('No RLP gateway endpoint for this instance') + else: + + return self._rlpgateway + @staticmethod def _get_info(target_endpoint: str, proxy: Optional[dict] = None, verify: bool = True) -> Info: info_response = CloudFoundryClient._check_response(requests.get('%s/v2/info' % target_endpoint, diff --git a/main/cloudfoundry_client/rlpgateway/__init__.py b/main/cloudfoundry_client/rlpgateway/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/main/cloudfoundry_client/rlpgateway/client.py b/main/cloudfoundry_client/rlpgateway/client.py new file mode 100644 index 0000000..e2ac930 --- /dev/null +++ b/main/cloudfoundry_client/rlpgateway/client.py @@ -0,0 +1,39 @@ +import logging +import aiohttp +from cloudfoundry_client.imported import urlparse + +_logger = logging.getLogger(__name__) + +class RLPGatewayClient(object): + """ + A client to read application logs directly from RLP gateway. + + The client is initialized with client id and client secret, + and provides functionality for asynchronous HTTP requests to RLP gateway endpoint. + """ + def __init__(self, rlp_gateway_endpoint, proxy, verify_ssl, credentials_manager): + self.proxy_host = None + self.proxy_port = None + self.rlp_gateway_endpoint = rlp_gateway_endpoint + self.verify_ssl = verify_ssl + self.credentials_manager = credentials_manager + + if proxy is not None and len(proxy) > 0: + proxy_domain = urlparse(proxy).netloc + idx = proxy_domain.find(':') + if 0 < idx < len(proxy_domain) - 2: + self.proxy_host = proxy_domain[:idx] + self.proxy_port = int(proxy_domain[idx + 1:]) + + async def stream_logs(self, app_guid): + url = '%s/v2/read?log&source_id=%s' % (self.rlp_gateway_endpoint, app_guid) + async with aiohttp.ClientSession() as session: + response = await session.get( + url=url, + headers={"Authorization": self.credentials_manager._access_token}, + ) + if response.status == 204: + yield {} + else: + async for data in response.content.iter_any(): + yield data diff --git a/requirements.txt b/requirements.txt index 626a53c..79bd69a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +aiohttp==3.6.2 protobuf==3.6.1 oauth2-client==1.2.0 websocket-client==0.53.0 From 85ad67191da9beedd07e10068755008e1a6608f6 Mon Sep 17 00:00:00 2001 From: Alina Golovchenko Date: Fri, 13 Mar 2020 11:22:15 +0100 Subject: [PATCH 022/264] RLP gateway client --- main/cloudfoundry_client/client.py | 74 +++++++++++------------------- 1 file changed, 27 insertions(+), 47 deletions(-) diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 3f00445..67d7252 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -1,19 +1,16 @@ import logging -from http import HTTPStatus -from typing import Optional import requests from oauth2_client.credentials_manager import CredentialManager, ServiceInformation -from requests import Response from cloudfoundry_client.doppler.client import DopplerClient from cloudfoundry_client.rlpgateway.client import RLPGatewayClient -from cloudfoundry_client.networking.v1.external.policies import PolicyManager from cloudfoundry_client.errors import InvalidStatusCode +from cloudfoundry_client.imported import UNAUTHORIZED from cloudfoundry_client.v2.apps import AppManager as AppManagerV2 -from cloudfoundry_client.v2.buildpacks import BuildpackManager as BuildpackManagerV2 -from cloudfoundry_client.v2.entities import EntityManager as EntityManagerV2 +from cloudfoundry_client.v2.buildpacks import BuildpackManager from cloudfoundry_client.v2.events import EventManager +from cloudfoundry_client.v2.entities import EntityManager as EntityManagerV2 from cloudfoundry_client.v2.jobs import JobManager from cloudfoundry_client.v2.resources import ResourceManager from cloudfoundry_client.v2.routes import RouteManager @@ -24,20 +21,14 @@ from cloudfoundry_client.v2.service_plan_visibilities import ServicePlanVisibilityManager from cloudfoundry_client.v2.service_plans import ServicePlanManager from cloudfoundry_client.v3.apps import AppManager as AppManagerV3 -from cloudfoundry_client.v3.buildpacks import BuildpackManager as BuildpackManagerV3 -from cloudfoundry_client.v3.domains import DomainManager from cloudfoundry_client.v3.entities import EntityManager as EntityManagerV3 -from cloudfoundry_client.v3.feature_flags import FeatureFlagManager -from cloudfoundry_client.v3.isolation_segments import IsolationSegmentManager -from cloudfoundry_client.v3.organizations import OrganizationManager -from cloudfoundry_client.v3.spaces import SpaceManager from cloudfoundry_client.v3.tasks import TaskManager _logger = logging.getLogger(__name__) class Info: - def __init__(self, api_version: str, authorization_endpoint: str, api_endpoint: str, doppler_endpoint: str): + def __init__(self, api_version, authorization_endpoint, api_endpoint, doppler_endpoint): self.api_version = api_version self.authorization_endpoint = authorization_endpoint self.api_endpoint = api_endpoint @@ -46,15 +37,10 @@ def __init__(self, api_version: str, authorization_endpoint: str, api_endpoint: self.rlp_gateway_endpoint = f'https://log-stream.{domain}' -class NetworkingV1External(object): - def __init__(self, target_endpoint: str, credential_manager: 'CloudFoundryClient'): - self.policies = PolicyManager(target_endpoint, credential_manager) - - class V2(object): - def __init__(self, target_endpoint: str, credential_manager: 'CloudFoundryClient'): + def __init__(self, target_endpoint, credential_manager): self.apps = AppManagerV2(target_endpoint, credential_manager) - self.buildpacks = BuildpackManagerV2(target_endpoint, credential_manager) + self.buildpacks = BuildpackManager(target_endpoint, credential_manager) self.jobs = JobManager(target_endpoint, credential_manager) self.service_bindings = ServiceBindingManager(target_endpoint, credential_manager) self.service_brokers = ServiceBrokerManager(target_endpoint, credential_manager) @@ -81,20 +67,16 @@ def __init__(self, target_endpoint: str, credential_manager: 'CloudFoundryClient class V3(object): - def __init__(self, target_endpoint: str, credential_manager: 'CloudFoundryClient'): + def __init__(self, target_endpoint, credential_manager): self.apps = AppManagerV3(target_endpoint, credential_manager) - self.buildpacks = BuildpackManagerV3(target_endpoint, credential_manager) - self.domains = DomainManager(target_endpoint, credential_manager) - self.feature_flags = FeatureFlagManager(target_endpoint, credential_manager) - self.isolation_segments = IsolationSegmentManager(target_endpoint, credential_manager) - self.spaces = SpaceManager(target_endpoint, credential_manager) - self.organizations = OrganizationManager(target_endpoint, credential_manager) + self.spaces = EntityManagerV3(target_endpoint, credential_manager, '/v3/spaces') + self.organizations = EntityManagerV3(target_endpoint, credential_manager, '/v3/organizations') self.service_instances = EntityManagerV3(target_endpoint, credential_manager, '/v3/service_instances') self.tasks = TaskManager(target_endpoint, credential_manager) class CloudFoundryClient(CredentialManager): - def __init__(self, target_endpoint: str, client_id: str = 'cf', client_secret: str = '', **kwargs): + def __init__(self, target_endpoint, client_id='cf', client_secret='', **kwargs): """" :param target_endpoint :the target endpoint :param client_id: the client_id @@ -115,15 +97,14 @@ def __init__(self, target_endpoint: str, client_id: str = 'cf', client_secret: s verify = kwargs.get('verify', True) self.token_format = kwargs.get('token_format') self.login_hint = kwargs.get('login_hint') - target_endpoint_trimmed = target_endpoint.rstrip('/') - info = self._get_info(target_endpoint_trimmed, proxy, verify=verify) + info = self._get_info(target_endpoint, proxy, verify=verify) if not info.api_version.startswith('2.'): raise AssertionError('Only version 2 is supported for now. Found %s' % info.api_version) service_information = ServiceInformation(None, '%s/oauth/token' % info.authorization_endpoint, client_id, client_secret, [], verify) super(CloudFoundryClient, self).__init__(service_information, proxies=proxy) - self.v2 = V2(target_endpoint_trimmed, self) - self.v3 = V3(target_endpoint_trimmed, self) + self.v2 = V2(target_endpoint, self) + self.v3 = V3(target_endpoint, self) self._doppler = DopplerClient(info.doppler_endpoint, self.proxies[ 'http' if info.doppler_endpoint.startswith('ws://') else 'https'], @@ -135,11 +116,10 @@ def __init__(self, target_endpoint: str, client_id: str = 'cf', client_secret: s self.service_information.verify, self, ) if info.rlp_gateway_endpoint is not None else None - self.networking_v1_external = NetworkingV1External(target_endpoint_trimmed, self) self.info = info @property - def doppler(self) -> DopplerClient: + def doppler(self): if self._doppler is None: raise NotImplementedError('No droppler endpoint for this instance') else: @@ -155,7 +135,7 @@ def rlpgateway(self): return self._rlpgateway @staticmethod - def _get_info(target_endpoint: str, proxy: Optional[dict] = None, verify: bool = True) -> Info: + def _get_info(target_endpoint, proxy=None, verify=True): info_response = CloudFoundryClient._check_response(requests.get('%s/v2/info' % target_endpoint, proxies=proxy if proxy is not None else dict( http='', https=''), @@ -167,20 +147,20 @@ def _get_info(target_endpoint: str, proxy: Optional[dict] = None, verify: bool = info.get('doppler_logging_endpoint')) @staticmethod - def _is_token_expired(response: Response) -> bool: - if response.status_code == HTTPStatus.UNAUTHORIZED.value: + def _is_token_expired(response): + if response.status_code == UNAUTHORIZED: try: json_data = response.json() result = json_data.get('code', 0) == 1000 and json_data.get('error_code', '') == 'CF-InvalidAuthToken' _logger.info('_is_token_expired - %s' % str(result)) return result - except BaseException as _: + except Exception as _: return False else: return False @staticmethod - def _token_request_headers(_) -> dict: + def _token_request_headers(_): return dict(Accept='application/json') def __getattr__(self, item): @@ -190,7 +170,7 @@ def __getattr__(self, item): else: raise AttributeError("type '%s' has no attribute '%s'" % (type(self).__name__, item)) - def _grant_password_request(self, login: str, password: str) -> dict: + def _grant_password_request(self, login, password): request = super(CloudFoundryClient, self)._grant_password_request(login, password) if self.token_format is not None: request['token_format'] = self.token_format @@ -198,34 +178,34 @@ def _grant_password_request(self, login: str, password: str) -> dict: request['login_hint'] = self.login_hint return request - def _grant_refresh_token_request(self, refresh_token: str) -> dict: + def _grant_refresh_token_request(self, refresh_token): request = super(CloudFoundryClient, self)._grant_refresh_token_request(refresh_token) if self.token_format is not None: request['token_format'] = self.token_format return request - def get(self, url: str, params: Optional[dict] = None, **kwargs) -> Response: + def get(self, url, params=None, **kwargs): response = super(CloudFoundryClient, self).get(url, params, **kwargs) return CloudFoundryClient._check_response(response) - def post(self, url: str, data=None, json=None, **kwargs) -> Response: + def post(self, url, data=None, json=None, **kwargs): response = super(CloudFoundryClient, self).post(url, data, json, **kwargs) return CloudFoundryClient._check_response(response) - def put(self, url: str, data=None, json=None, **kwargs) -> Response: + def put(self, url, data=None, json=None, **kwargs): response = super(CloudFoundryClient, self).put(url, data, json, **kwargs) return CloudFoundryClient._check_response(response) - def patch(self, url: str, data=None, json=None, **kwargs) -> Response: + def patch(self, url, data=None, json=None, **kwargs): response = super(CloudFoundryClient, self).patch(url, data, json, **kwargs) return CloudFoundryClient._check_response(response) - def delete(self, url: str, **kwargs) -> Response: + def delete(self, url, **kwargs): response = super(CloudFoundryClient, self).delete(url, **kwargs) return CloudFoundryClient._check_response(response) @staticmethod - def _check_response(response: Response) -> Response: + def _check_response(response): if int(response.status_code / 100) == 2: return response else: From f0c54c38b6cd7c4854c567158fa25dec8ab86f6a Mon Sep 17 00:00:00 2001 From: Alina Golovchenko Date: Wed, 18 Mar 2020 09:23:23 +0100 Subject: [PATCH 023/264] Log-stream link --- main/cloudfoundry_client/client.py | 92 +++++++++++++++++++----------- 1 file changed, 58 insertions(+), 34 deletions(-) diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 67d7252..08bd4b9 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -1,16 +1,19 @@ import logging +from http import HTTPStatus +from typing import Optional import requests from oauth2_client.credentials_manager import CredentialManager, ServiceInformation +from requests import Response from cloudfoundry_client.doppler.client import DopplerClient from cloudfoundry_client.rlpgateway.client import RLPGatewayClient +from cloudfoundry_client.networking.v1.external.policies import PolicyManager from cloudfoundry_client.errors import InvalidStatusCode -from cloudfoundry_client.imported import UNAUTHORIZED from cloudfoundry_client.v2.apps import AppManager as AppManagerV2 -from cloudfoundry_client.v2.buildpacks import BuildpackManager -from cloudfoundry_client.v2.events import EventManager +from cloudfoundry_client.v2.buildpacks import BuildpackManager as BuildpackManagerV2 from cloudfoundry_client.v2.entities import EntityManager as EntityManagerV2 +from cloudfoundry_client.v2.events import EventManager from cloudfoundry_client.v2.jobs import JobManager from cloudfoundry_client.v2.resources import ResourceManager from cloudfoundry_client.v2.routes import RouteManager @@ -21,26 +24,36 @@ from cloudfoundry_client.v2.service_plan_visibilities import ServicePlanVisibilityManager from cloudfoundry_client.v2.service_plans import ServicePlanManager from cloudfoundry_client.v3.apps import AppManager as AppManagerV3 +from cloudfoundry_client.v3.buildpacks import BuildpackManager as BuildpackManagerV3 +from cloudfoundry_client.v3.domains import DomainManager from cloudfoundry_client.v3.entities import EntityManager as EntityManagerV3 +from cloudfoundry_client.v3.feature_flags import FeatureFlagManager +from cloudfoundry_client.v3.isolation_segments import IsolationSegmentManager +from cloudfoundry_client.v3.organizations import OrganizationManager +from cloudfoundry_client.v3.spaces import SpaceManager from cloudfoundry_client.v3.tasks import TaskManager _logger = logging.getLogger(__name__) class Info: - def __init__(self, api_version, authorization_endpoint, api_endpoint, doppler_endpoint): + def __init__(self, api_version: str, authorization_endpoint: str, api_endpoint: str, doppler_endpoint: str, log_stream_endpoint: str): self.api_version = api_version self.authorization_endpoint = authorization_endpoint self.api_endpoint = api_endpoint self.doppler_endpoint = doppler_endpoint - domain = '.'.join(api_endpoint.split('.')[1:]) - self.rlp_gateway_endpoint = f'https://log-stream.{domain}' + self.log_stream_endpoint = log_stream_endpoint + + +class NetworkingV1External(object): + def __init__(self, target_endpoint: str, credential_manager: 'CloudFoundryClient'): + self.policies = PolicyManager(target_endpoint, credential_manager) class V2(object): - def __init__(self, target_endpoint, credential_manager): + def __init__(self, target_endpoint: str, credential_manager: 'CloudFoundryClient'): self.apps = AppManagerV2(target_endpoint, credential_manager) - self.buildpacks = BuildpackManager(target_endpoint, credential_manager) + self.buildpacks = BuildpackManagerV2(target_endpoint, credential_manager) self.jobs = JobManager(target_endpoint, credential_manager) self.service_bindings = ServiceBindingManager(target_endpoint, credential_manager) self.service_brokers = ServiceBrokerManager(target_endpoint, credential_manager) @@ -67,16 +80,20 @@ def __init__(self, target_endpoint, credential_manager): class V3(object): - def __init__(self, target_endpoint, credential_manager): + def __init__(self, target_endpoint: str, credential_manager: 'CloudFoundryClient'): self.apps = AppManagerV3(target_endpoint, credential_manager) - self.spaces = EntityManagerV3(target_endpoint, credential_manager, '/v3/spaces') - self.organizations = EntityManagerV3(target_endpoint, credential_manager, '/v3/organizations') + self.buildpacks = BuildpackManagerV3(target_endpoint, credential_manager) + self.domains = DomainManager(target_endpoint, credential_manager) + self.feature_flags = FeatureFlagManager(target_endpoint, credential_manager) + self.isolation_segments = IsolationSegmentManager(target_endpoint, credential_manager) + self.spaces = SpaceManager(target_endpoint, credential_manager) + self.organizations = OrganizationManager(target_endpoint, credential_manager) self.service_instances = EntityManagerV3(target_endpoint, credential_manager, '/v3/service_instances') self.tasks = TaskManager(target_endpoint, credential_manager) class CloudFoundryClient(CredentialManager): - def __init__(self, target_endpoint, client_id='cf', client_secret='', **kwargs): + def __init__(self, target_endpoint: str, client_id: str = 'cf', client_secret: str = '', **kwargs): """" :param target_endpoint :the target endpoint :param client_id: the client_id @@ -97,29 +114,30 @@ def __init__(self, target_endpoint, client_id='cf', client_secret='', **kwargs): verify = kwargs.get('verify', True) self.token_format = kwargs.get('token_format') self.login_hint = kwargs.get('login_hint') - info = self._get_info(target_endpoint, proxy, verify=verify) + target_endpoint_trimmed = target_endpoint.rstrip('/') + info = self._get_info(target_endpoint_trimmed, proxy, verify=verify) if not info.api_version.startswith('2.'): raise AssertionError('Only version 2 is supported for now. Found %s' % info.api_version) service_information = ServiceInformation(None, '%s/oauth/token' % info.authorization_endpoint, client_id, client_secret, [], verify) super(CloudFoundryClient, self).__init__(service_information, proxies=proxy) - self.v2 = V2(target_endpoint, self) - self.v3 = V3(target_endpoint, self) + self.v2 = V2(target_endpoint_trimmed, self) + self.v3 = V3(target_endpoint_trimmed, self) self._doppler = DopplerClient(info.doppler_endpoint, self.proxies[ 'http' if info.doppler_endpoint.startswith('ws://') else 'https'], self.service_information.verify, self) if info.doppler_endpoint is not None else None - self._rlpgateway = RLPGatewayClient(info.rlp_gateway_endpoint, - self.proxies[ - 'http' if info.rlp_gateway_endpoint.startswith('ws://') else 'https'], + self._rlpgateway = RLPGatewayClient(info.log_stream_endpoint, + self.proxies['https'], self.service_information.verify, self, - ) if info.rlp_gateway_endpoint is not None else None + ) if info.log_stream_endpoint is not None else None + self.networking_v1_external = NetworkingV1External(target_endpoint_trimmed, self) self.info = info @property - def doppler(self): + def doppler(self) -> DopplerClient: if self._doppler is None: raise NotImplementedError('No droppler endpoint for this instance') else: @@ -135,32 +153,38 @@ def rlpgateway(self): return self._rlpgateway @staticmethod - def _get_info(target_endpoint, proxy=None, verify=True): + def _get_info(target_endpoint: str, proxy: Optional[dict] = None, verify: bool = True) -> Info: info_response = CloudFoundryClient._check_response(requests.get('%s/v2/info' % target_endpoint, proxies=proxy if proxy is not None else dict( http='', https=''), verify=verify)) + links_response = CloudFoundryClient._check_response(requests.get('%s/' % target_endpoint, + proxies=proxy if proxy is not None else dict( + http='', https=''), + verify=verify)) info = info_response.json() + links = links_response.json() return Info(info['api_version'], info['authorization_endpoint'], target_endpoint, - info.get('doppler_logging_endpoint')) + info.get('doppler_logging_endpoint'), + links['links'].get('log_stream').get('href')) @staticmethod - def _is_token_expired(response): - if response.status_code == UNAUTHORIZED: + def _is_token_expired(response: Response) -> bool: + if response.status_code == HTTPStatus.UNAUTHORIZED.value: try: json_data = response.json() result = json_data.get('code', 0) == 1000 and json_data.get('error_code', '') == 'CF-InvalidAuthToken' _logger.info('_is_token_expired - %s' % str(result)) return result - except Exception as _: + except BaseException as _: return False else: return False @staticmethod - def _token_request_headers(_): + def _token_request_headers(_) -> dict: return dict(Accept='application/json') def __getattr__(self, item): @@ -170,7 +194,7 @@ def __getattr__(self, item): else: raise AttributeError("type '%s' has no attribute '%s'" % (type(self).__name__, item)) - def _grant_password_request(self, login, password): + def _grant_password_request(self, login: str, password: str) -> dict: request = super(CloudFoundryClient, self)._grant_password_request(login, password) if self.token_format is not None: request['token_format'] = self.token_format @@ -178,34 +202,34 @@ def _grant_password_request(self, login, password): request['login_hint'] = self.login_hint return request - def _grant_refresh_token_request(self, refresh_token): + def _grant_refresh_token_request(self, refresh_token: str) -> dict: request = super(CloudFoundryClient, self)._grant_refresh_token_request(refresh_token) if self.token_format is not None: request['token_format'] = self.token_format return request - def get(self, url, params=None, **kwargs): + def get(self, url: str, params: Optional[dict] = None, **kwargs) -> Response: response = super(CloudFoundryClient, self).get(url, params, **kwargs) return CloudFoundryClient._check_response(response) - def post(self, url, data=None, json=None, **kwargs): + def post(self, url: str, data=None, json=None, **kwargs) -> Response: response = super(CloudFoundryClient, self).post(url, data, json, **kwargs) return CloudFoundryClient._check_response(response) - def put(self, url, data=None, json=None, **kwargs): + def put(self, url: str, data=None, json=None, **kwargs) -> Response: response = super(CloudFoundryClient, self).put(url, data, json, **kwargs) return CloudFoundryClient._check_response(response) - def patch(self, url, data=None, json=None, **kwargs): + def patch(self, url: str, data=None, json=None, **kwargs) -> Response: response = super(CloudFoundryClient, self).patch(url, data, json, **kwargs) return CloudFoundryClient._check_response(response) - def delete(self, url, **kwargs): + def delete(self, url: str, **kwargs) -> Response: response = super(CloudFoundryClient, self).delete(url, **kwargs) return CloudFoundryClient._check_response(response) @staticmethod - def _check_response(response): + def _check_response(response: Response) -> Response: if int(response.status_code / 100) == 2: return response else: From 40c8ac79c4dc1aac91fdd8639ab3c9c5dcc5144a Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Wed, 18 Mar 2020 17:34:16 +0100 Subject: [PATCH 024/264] Big refactoring * Add unit tests on get info * information is got from a mixed of '/' and '/info' * oauth token requests are made from the 'token_endpoint' got from '/info' * allow oauth grant code flow by using 'authorization_endpoint' got from '/info' * doppler endpoint is got from '/' 'logging.href' entry just as 'log_stream' entry --- README.rst | 20 ++++ main/cloudfoundry_client/client.py | 36 +++--- main/cloudfoundry_client/rlpgateway/client.py | 11 +- test/__init__.py | 3 +- test/abstract_test_case.py | 69 ++++++++++- test/fake_requests.py | 27 ----- test/fixtures/fake/manifest_main.yml | 26 ++-- test/fixtures/operations/manifest.yml | 4 +- .../v2/apps/GET_{id}_env_response.json | 3 - .../GET_{id}_service_bindings_response.json | 1 - .../v2/apps/GET_{id}_summary_response.json | 113 +++++++++--------- .../v2/service_bindings/GET_response.json | 1 - .../service_bindings/GET_{id}_response.json | 1 - .../v2/service_bindings/POST_response.json | 1 - .../v2/service_instances/POST_response.json | 2 - .../service_instances/PUT_{id}_response.json | 1 - .../v2/services/GET_{id}_response.json | 2 - test/fixtures/v3/apps/GET_response.json | 4 +- .../POST_{id}_actions_start_response.json | 4 +- .../v3/buildpacks/GET_{id}_response.json | 8 +- .../v3/buildpacks/PATCH_{id}_response.json | 8 +- .../fixtures/v3/buildpacks/POST_response.json | 8 +- .../v3/domains/GET_{id}_response.json | 16 ++- .../v3/domains/PATCH_{id}_response.json | 20 +++- test/fixtures/v3/domains/POST_response.json | 16 ++- ...onships_shared_organizations_response.json | 8 +- .../GET_{id}_default_domain_response.json | 16 ++- .../v3/service_instances/GET_response.json | 4 +- .../service_instances/GET_{id}_response.json | 4 +- test/networking/v1/external/test_policies.py | 32 ++--- test/operations/push/test_push.py | 8 +- .../push/validation/test_manifest_reader.py | 2 +- test/test_client.py | 43 +++---- test/v2/test_apps.py | 92 +++++++------- test/v2/test_buildpacks.py | 13 +- test/v2/test_doppler.py | 13 +- test/v2/test_entities.py | 36 +++--- test/v2/test_events.py | 10 +- test/v2/test_organizations.py | 18 ++- test/v2/test_routes.py | 38 +++--- test/v2/test_service_bindings.py | 34 +++--- test/v2/test_service_brokers.py | 38 +++--- test/v2/test_service_instances.py | 52 ++++---- test/v2/test_service_keys.py | 29 +++-- test/v2/test_service_plan_visibilities.py | 30 ++--- test/v2/test_service_plans.py | 32 ++--- test/v2/test_services.py | 35 +++--- test/v2/test_spaces.py | 35 +++--- test/v3/test_apps.py | 33 +++-- test/v3/test_buildpacks.py | 39 +++--- test/v3/test_domains.py | 45 ++++--- test/v3/test_feature_flags.py | 19 ++- test/v3/test_isolation_segments.py | 32 +++-- test/v3/test_organizations.py | 60 +++++----- test/v3/test_service_instances.py | 19 ++- test/v3/test_spaces.py | 50 ++++---- test/v3/test_tasks.py | 45 ++++--- 57 files changed, 719 insertions(+), 650 deletions(-) diff --git a/README.rst b/README.rst index 3e2022a..9dbdffb 100644 --- a/README.rst +++ b/README.rst @@ -68,6 +68,26 @@ To instantiate the client, nothing easier client.refresh_token = 'refresh-token' client._access_token = 'access-token' +It can also be instantiated with oauth code flow if you possess a dedicated oauth application with its redirection + +.. code-block:: python + from flask import request + from cloudfoundry_client.client import CloudFoundryClient + target_endpoint = 'https://somewhere.org' + proxy = dict(http=os.environ.get('HTTP_PROXY', ''), https=os.environ.get('HTTPS_PROXY', '')) + client = CloudFoundryClient(target_endpoint, proxy=proxy, verify=False, client_id='my-client-id', client_secret='my-client-secret') + + @app.route('/login') + def login(): + global client + return redirect(client.generate_authorize_url('http://localhost:9999/code', '666')) + + @app.route('/code') + def code(): + global client + client.init_authorize_code_process('http://localhost:9999/code', request.args.get('code')) + + And then you can use it as follows: .. code-block:: python diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 08bd4b9..69b4ed6 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -7,9 +7,9 @@ from requests import Response from cloudfoundry_client.doppler.client import DopplerClient -from cloudfoundry_client.rlpgateway.client import RLPGatewayClient -from cloudfoundry_client.networking.v1.external.policies import PolicyManager from cloudfoundry_client.errors import InvalidStatusCode +from cloudfoundry_client.networking.v1.external.policies import PolicyManager +from cloudfoundry_client.rlpgateway.client import RLPGatewayClient from cloudfoundry_client.v2.apps import AppManager as AppManagerV2 from cloudfoundry_client.v2.buildpacks import BuildpackManager as BuildpackManagerV2 from cloudfoundry_client.v2.entities import EntityManager as EntityManagerV2 @@ -37,9 +37,15 @@ class Info: - def __init__(self, api_version: str, authorization_endpoint: str, api_endpoint: str, doppler_endpoint: str, log_stream_endpoint: str): - self.api_version = api_version + def __init__(self, api_v2_version: str, + authorization_endpoint: str, + token_endpoint: str, + api_endpoint: str, + doppler_endpoint: str, + log_stream_endpoint: str): + self.api_v2_version = api_v2_version self.authorization_endpoint = authorization_endpoint + self.token_endpoint = token_endpoint self.api_endpoint = api_endpoint self.doppler_endpoint = doppler_endpoint self.log_stream_endpoint = log_stream_endpoint @@ -116,9 +122,10 @@ def __init__(self, target_endpoint: str, client_id: str = 'cf', client_secret: s self.login_hint = kwargs.get('login_hint') target_endpoint_trimmed = target_endpoint.rstrip('/') info = self._get_info(target_endpoint_trimmed, proxy, verify=verify) - if not info.api_version.startswith('2.'): - raise AssertionError('Only version 2 is supported for now. Found %s' % info.api_version) - service_information = ServiceInformation(None, '%s/oauth/token' % info.authorization_endpoint, + if not info.api_v2_version.startswith('2.'): + raise AssertionError('Only version 2 is supported for now. Found %s' % info.api_v2_version) + service_information = ServiceInformation('%s/oauth/authorize' % info.authorization_endpoint, + '%s/oauth/token' % info.token_endpoint, client_id, client_secret, [], verify) super(CloudFoundryClient, self).__init__(service_information, proxies=proxy) self.v2 = V2(target_endpoint_trimmed, self) @@ -154,20 +161,21 @@ def rlpgateway(self): @staticmethod def _get_info(target_endpoint: str, proxy: Optional[dict] = None, verify: bool = True) -> Info: - info_response = CloudFoundryClient._check_response(requests.get('%s/v2/info' % target_endpoint, - proxies=proxy if proxy is not None else dict( - http='', https=''), - verify=verify)) - links_response = CloudFoundryClient._check_response(requests.get('%s/' % target_endpoint, + info_response = CloudFoundryClient._check_response(requests.get('%s/info' % target_endpoint, proxies=proxy if proxy is not None else dict( http='', https=''), verify=verify)) info = info_response.json() + links_response = CloudFoundryClient._check_response(requests.get('%s/' % target_endpoint, + proxies=proxy if proxy is not None else dict( + http='', https=''), + verify=verify)) links = links_response.json() - return Info(info['api_version'], + return Info(links['links']['cloud_controller_v2']['meta']['version'], info['authorization_endpoint'], + info['token_endpoint'], target_endpoint, - info.get('doppler_logging_endpoint'), + links['links'].get('logging').get('href'), links['links'].get('log_stream').get('href')) @staticmethod diff --git a/main/cloudfoundry_client/rlpgateway/client.py b/main/cloudfoundry_client/rlpgateway/client.py index e2ac930..a5b201c 100644 --- a/main/cloudfoundry_client/rlpgateway/client.py +++ b/main/cloudfoundry_client/rlpgateway/client.py @@ -1,9 +1,11 @@ import logging +from urllib.parse import urlparse + import aiohttp -from cloudfoundry_client.imported import urlparse _logger = logging.getLogger(__name__) + class RLPGatewayClient(object): """ A client to read application logs directly from RLP gateway. @@ -11,6 +13,7 @@ class RLPGatewayClient(object): The client is initialized with client id and client secret, and provides functionality for asynchronous HTTP requests to RLP gateway endpoint. """ + def __init__(self, rlp_gateway_endpoint, proxy, verify_ssl, credentials_manager): self.proxy_host = None self.proxy_port = None @@ -29,9 +32,9 @@ async def stream_logs(self, app_guid): url = '%s/v2/read?log&source_id=%s' % (self.rlp_gateway_endpoint, app_guid) async with aiohttp.ClientSession() as session: response = await session.get( - url=url, - headers={"Authorization": self.credentials_manager._access_token}, - ) + url=url, + headers={"Authorization": self.credentials_manager._access_token}, + ) if response.status == 204: yield {} else: diff --git a/test/__init__.py b/test/__init__.py index 247398a..811245e 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -1,9 +1,8 @@ #!/usr/bin/env python -import os -import sys import unittest + def get_suite(): loader = unittest.TestLoader() suite = loader.loadTestsFromModule(__package__) diff --git a/test/abstract_test_case.py b/test/abstract_test_case.py index 92d5b88..818a68a 100644 --- a/test/abstract_test_case.py +++ b/test/abstract_test_case.py @@ -1,11 +1,14 @@ import json +import os from http import HTTPStatus +from typing import Optional from unittest.mock import MagicMock, patch from oauth2_client.credentials_manager import CredentialManager from cloudfoundry_client.client import CloudFoundryClient -from fake_requests import TARGET_ENDPOINT, mock_response +from fake_requests import MockResponse + def mock_cloudfoundry_client_class(): @@ -23,14 +26,68 @@ def __init__(self, *args, **kwargs): class AbstractTestCase(object): + TARGET_ENDPOINT = "http://somewhere.org" + AUTHORIZATION_ENDPOINT = "http://login.somewhere.org" + TOKEN_ENDPOINT = "http://token.somewhere.org" + DOPPLER_ENDPOINT = 'wss://doppler.nd-cfapi.itn.ftgroup:443' + LOG_STREAM_ENDPOINT = 'https://log-stream.nd-cfapi.itn.ftgroup' + API_V2_VERSION = "2.141.0" + API_V3_VERSION = "3.76.0" + @classmethod def mock_client_class(cls): mock_cloudfoundry_client_class() def build_client(self): with patch('cloudfoundry_client.client.requests') as fake_requests: - fake_info_response = mock_response('/v2/info', HTTPStatus.OK, None) - fake_info_response.text = json.dumps(dict(api_version='2.X', - authorization_endpoint=TARGET_ENDPOINT)) - fake_requests.get.return_value = fake_info_response - self.client = CloudFoundryClient(TARGET_ENDPOINT) + self._mock_info_calls(fake_requests) + self.client = CloudFoundryClient(self.TARGET_ENDPOINT) + + @staticmethod + def _mock_info_calls(requests): + requests.get.side_effect = [ + MockResponse( + '%s/v2/info' % AbstractTestCase.TARGET_ENDPOINT, + status_code=HTTPStatus.OK, + text=json.dumps(dict(authorization_endpoint=AbstractTestCase.AUTHORIZATION_ENDPOINT, + token_endpoint=AbstractTestCase.TOKEN_ENDPOINT))), + MockResponse( + '%s/' % AbstractTestCase.TARGET_ENDPOINT, + status_code=HTTPStatus.OK, + text=json.dumps(dict(links={ + 'self': dict(href=AbstractTestCase.TARGET_ENDPOINT), + 'cloud_controller_v2': dict(href='%s/v2' % AbstractTestCase.TARGET_ENDPOINT, + meta=dict(version=AbstractTestCase.API_V2_VERSION)), + 'cloud_controller_v3': dict(href='%s/v3' % AbstractTestCase.TARGET_ENDPOINT, + meta=dict(version=AbstractTestCase.API_V3_VERSION)), + 'logging': dict(href=AbstractTestCase.DOPPLER_ENDPOINT), + 'log_stream': dict(href=AbstractTestCase.LOG_STREAM_ENDPOINT), + 'app_ssh': dict(href='ssh.nd-cfapi.itn.ftgroup:80'), + 'uaa': dict(href='https://uaa.nd-cfapi.itn.ftgroup'), + 'network_policy_v0': dict( + href='https://api.nd-cfapi.itn.ftgroup/networking/v0/external'), + 'network_policy_v1': dict( + href='https://api.nd-cfapi.itn.ftgroup/networking/v1/external') + }))) + ] + + @staticmethod + def get_fixtures_path(*paths): + return os.path.join(os.path.dirname(__file__), 'fixtures', *paths) + + @staticmethod + def mock_response(uri: str, status_code: HTTPStatus, headers: Optional[dict], *path_parts: str): + if len(path_parts) > 0: + file_name = path_parts[len(path_parts) - 1] + extension_idx = file_name.rfind('.') + binary_file = extension_idx >= 0 and file_name[extension_idx:] == '.bin' + with(open(AbstractTestCase.get_fixtures_path(*path_parts), + 'rb' if binary_file else 'r')) as f: + return MockResponse(url='%s%s' % (AbstractTestCase.TARGET_ENDPOINT, uri), + status_code=status_code, + text=f.read(), + headers=headers) + else: + return MockResponse('%s%s' % (AbstractTestCase.TARGET_ENDPOINT, uri), + status_code, + '') diff --git a/test/fake_requests.py b/test/fake_requests.py index 0849b20..0f44feb 100644 --- a/test/fake_requests.py +++ b/test/fake_requests.py @@ -1,7 +1,5 @@ -import os from http import HTTPStatus from json import loads -from typing import Optional from unittest.mock import MagicMock @@ -38,31 +36,6 @@ def __iter__(self): return iterate_text(self.text) -TARGET_ENDPOINT = "http://somewhere.org" - - -def get_fixtures_path(*paths): - return os.path.join(os.path.dirname(__file__), 'fixtures', *paths) - - -def mock_response(uri: str, status_code: HTTPStatus, headers: Optional[dict], *path_parts: str): - global TARGET_ENDPOINT - if len(path_parts) > 0: - file_name = path_parts[len(path_parts) - 1] - extension_idx = file_name.rfind('.') - binary_file = extension_idx >= 0 and file_name[extension_idx:] == '.bin' - with(open(get_fixtures_path(*path_parts), - 'rb' if binary_file else 'r')) as f: - return MockResponse(url='%s%s' % (TARGET_ENDPOINT, uri), - status_code=status_code, - text=f.read(), - headers=headers) - else: - return MockResponse('%s%s' % (TARGET_ENDPOINT, uri), - status_code, - '') - - class FakeRequests(object): def __init__(self): self.Session = MagicMock() diff --git a/test/fixtures/fake/manifest_main.yml b/test/fixtures/fake/manifest_main.yml index 729ac70..710d377 100644 --- a/test/fixtures/fake/manifest_main.yml +++ b/test/fixtures/fake/manifest_main.yml @@ -1,16 +1,16 @@ --- applications: -- name: test_app - memory: 1024M - instances: 1 - path: . - health-check-type: http - health-check-http-endpoint: /manage/health - services: - - service1 - - service2 - no-route: true - env: - VAR1: VALUE1 - VAR2: VALUE2 + - name: test_app + memory: 1024M + instances: 1 + path: . + health-check-type: http + health-check-http-endpoint: /manage/health + services: + - service1 + - service2 + no-route: true + env: + VAR1: VALUE1 + VAR2: VALUE2 diff --git a/test/fixtures/operations/manifest.yml b/test/fixtures/operations/manifest.yml index 7556413..e6434de 100644 --- a/test/fixtures/operations/manifest.yml +++ b/test/fixtures/operations/manifest.yml @@ -1,8 +1,8 @@ applications: - name: the-name routes: - - route: first-route - - route: second-route + - route: first-route + - route: second-route docker: username: the-user password: P@SsW0r$ diff --git a/test/fixtures/v2/apps/GET_{id}_env_response.json b/test/fixtures/v2/apps/GET_{id}_env_response.json index 674dbc7..6f8f5aa 100644 --- a/test/fixtures/v2/apps/GET_{id}_env_response.json +++ b/test/fixtures/v2/apps/GET_{id}_env_response.json @@ -10,7 +10,6 @@ }, "system_env_json": { "VCAP_SERVICES": { - } }, "application_env_json": { @@ -24,14 +23,12 @@ "application_version": "45919b1d-422d-4953-a0a3-91d7fd2899bf", "application_name": "name-243", "application_uris": [ - ], "version": "45919b1d-422d-4953-a0a3-91d7fd2899bf", "name": "name-243", "space_name": "name-244", "space_id": "885c392a-3502-4cff-a47d-49089b13b1b7", "uris": [ - ], "users": null } diff --git a/test/fixtures/v2/apps/GET_{id}_service_bindings_response.json b/test/fixtures/v2/apps/GET_{id}_service_bindings_response.json index 1f1e0e9..5b13acc 100644 --- a/test/fixtures/v2/apps/GET_{id}_service_bindings_response.json +++ b/test/fixtures/v2/apps/GET_{id}_service_bindings_response.json @@ -18,7 +18,6 @@ "creds-key-384": "creds-val-384" }, "binding_options": { - }, "gateway_data": null, "gateway_name": "", diff --git a/test/fixtures/v2/apps/GET_{id}_summary_response.json b/test/fixtures/v2/apps/GET_{id}_summary_response.json index 5bcb834..91ba54f 100644 --- a/test/fixtures/v2/apps/GET_{id}_summary_response.json +++ b/test/fixtures/v2/apps/GET_{id}_summary_response.json @@ -1,60 +1,59 @@ - { - "version": "0b9e28b8-b69d-46ba-ad7e-6c83899826b2", - "staging_failed_reason": null, - "docker_credentials_json": { - "redacted_message": "[PRIVATE DATA HIDDEN]" - }, - "staging_failed_description": null, - "instances": 1, - "guid": "a5eee659-56a7-4123-91ac-ba190ddc5477", - "docker_image": null, - "diego": true, - "console": false, - "package_state": "STAGED", - "state": "STOPPED", - "production": false, - "stack_guid": "3cb62b55-3230-45d1-a297-ace25b3e3479", - "memory": 512, - "package_updated_at": "2016-08-10T14:01:54Z", - "staging_task_id": "c80457643d7d455c93f92151d6384c2d", - "buildpack": null, - "enable_ssh": true, - "detected_start_command": "sh boot.sh", - "disk_quota": 1024, - "routes": [ - { - "path": "", - "host": "application_name", - "guid": "6c7d07f9-57a1-474e-8e91-82a597550956", - "port": null, - "domain": { - "guid": "f030f4bb-2b60-47e9-be3e-8105655c0286", - "name": "some-domain" - } - } - ], - "services": [], - "detected_buildpack": "staticfile 1.3.8", - "space_guid": "6c99652b-4795-416c-9466-64a3e4555627", - "name": "application_name", - "running_instances": 0, - "health_check_type": "port", - "command": null, - "debug": null, - "available_domains": [ - { - "guid": "255b9370-3836-402c-9e1f-08b2a96dd180", - "name": "some-domain", - "owning_organization_guid": "d7d77408-a250-45e3-8de5-71fcf199bbab" + "version": "0b9e28b8-b69d-46ba-ad7e-6c83899826b2", + "staging_failed_reason": null, + "docker_credentials_json": { + "redacted_message": "[PRIVATE DATA HIDDEN]" }, - { - "guid": "f030f4bb-2b60-47e9-be3e-8105655c0286", - "name": "other-domain", - "owning_organization_guid": "d7d77408-a250-45e3-8de5-71fcf199bbab" - } - ], - "environment_json": {}, - "ports": null, - "health_check_timeout": null + "staging_failed_description": null, + "instances": 1, + "guid": "a5eee659-56a7-4123-91ac-ba190ddc5477", + "docker_image": null, + "diego": true, + "console": false, + "package_state": "STAGED", + "state": "STOPPED", + "production": false, + "stack_guid": "3cb62b55-3230-45d1-a297-ace25b3e3479", + "memory": 512, + "package_updated_at": "2016-08-10T14:01:54Z", + "staging_task_id": "c80457643d7d455c93f92151d6384c2d", + "buildpack": null, + "enable_ssh": true, + "detected_start_command": "sh boot.sh", + "disk_quota": 1024, + "routes": [ + { + "path": "", + "host": "application_name", + "guid": "6c7d07f9-57a1-474e-8e91-82a597550956", + "port": null, + "domain": { + "guid": "f030f4bb-2b60-47e9-be3e-8105655c0286", + "name": "some-domain" + } + } + ], + "services": [], + "detected_buildpack": "staticfile 1.3.8", + "space_guid": "6c99652b-4795-416c-9466-64a3e4555627", + "name": "application_name", + "running_instances": 0, + "health_check_type": "port", + "command": null, + "debug": null, + "available_domains": [ + { + "guid": "255b9370-3836-402c-9e1f-08b2a96dd180", + "name": "some-domain", + "owning_organization_guid": "d7d77408-a250-45e3-8de5-71fcf199bbab" + }, + { + "guid": "f030f4bb-2b60-47e9-be3e-8105655c0286", + "name": "other-domain", + "owning_organization_guid": "d7d77408-a250-45e3-8de5-71fcf199bbab" + } + ], + "environment_json": {}, + "ports": null, + "health_check_timeout": null } \ No newline at end of file diff --git a/test/fixtures/v2/service_bindings/GET_response.json b/test/fixtures/v2/service_bindings/GET_response.json index 1f1e0e9..5b13acc 100644 --- a/test/fixtures/v2/service_bindings/GET_response.json +++ b/test/fixtures/v2/service_bindings/GET_response.json @@ -18,7 +18,6 @@ "creds-key-384": "creds-val-384" }, "binding_options": { - }, "gateway_data": null, "gateway_name": "", diff --git a/test/fixtures/v2/service_bindings/GET_{id}_response.json b/test/fixtures/v2/service_bindings/GET_{id}_response.json index b34c620..17d034b 100644 --- a/test/fixtures/v2/service_bindings/GET_{id}_response.json +++ b/test/fixtures/v2/service_bindings/GET_{id}_response.json @@ -12,7 +12,6 @@ "creds-key-376": "creds-val-376" }, "binding_options": { - }, "gateway_data": null, "gateway_name": "", diff --git a/test/fixtures/v2/service_bindings/POST_response.json b/test/fixtures/v2/service_bindings/POST_response.json index 4b19ead..9500b9f 100644 --- a/test/fixtures/v2/service_bindings/POST_response.json +++ b/test/fixtures/v2/service_bindings/POST_response.json @@ -12,7 +12,6 @@ "creds-key-390": "creds-val-390" }, "binding_options": { - }, "gateway_data": null, "gateway_name": "", diff --git a/test/fixtures/v2/service_instances/POST_response.json b/test/fixtures/v2/service_instances/POST_response.json index 62428f3..f990438 100644 --- a/test/fixtures/v2/service_instances/POST_response.json +++ b/test/fixtures/v2/service_instances/POST_response.json @@ -8,7 +8,6 @@ "entity": { "name": "test_name", "credentials": { - }, "service_plan_guid": "ce1aeaa9-c5ef-417e-abf7-b16f07724aac", "space_guid": "2d745a4b-67e3-4398-986e-2adbcf8f7ec9", @@ -23,7 +22,6 @@ "created_at": "2016-07-23T12:04:33Z" }, "tags": [ - ], "space_url": "/v2/spaces/2d745a4b-67e3-4398-986e-2adbcf8f7ec9", "service_plan_url": "/v2/service_plans/ce1aeaa9-c5ef-417e-abf7-b16f07724aac", diff --git a/test/fixtures/v2/service_instances/PUT_{id}_response.json b/test/fixtures/v2/service_instances/PUT_{id}_response.json index cece18f..99f5624 100644 --- a/test/fixtures/v2/service_instances/PUT_{id}_response.json +++ b/test/fixtures/v2/service_instances/PUT_{id}_response.json @@ -23,7 +23,6 @@ "created_at": "2015-11-30T23:39:01Z" }, "tags": [ - ], "space_url": "/v2/spaces/3a46f977-c962-4445-8086-c3410a38dd2f", "service_plan_url": "/v2/service_plans/cf23d3e3-5f83-45db-be33-3a6a13203b43", diff --git a/test/fixtures/v2/services/GET_{id}_response.json b/test/fixtures/v2/services/GET_{id}_response.json index 3bdbcfd..a2da462 100644 --- a/test/fixtures/v2/services/GET_{id}_response.json +++ b/test/fixtures/v2/services/GET_{id}_response.json @@ -18,10 +18,8 @@ "unique_id": "b34d6452-7ee5-40c0-b089-71357ba760e7", "extra": null, "tags": [ - ], "requires": [ - ], "documentation_url": null, "service_broker_guid": "68e0a454-d42c-410e-b566-9f7806fe87aa", diff --git a/test/fixtures/v3/apps/GET_response.json b/test/fixtures/v3/apps/GET_response.json index d07436c..faec509 100644 --- a/test/fixtures/v3/apps/GET_response.json +++ b/test/fixtures/v3/apps/GET_response.json @@ -84,7 +84,9 @@ "lifecycle": { "type": "buildpack", "data": { - "buildpacks": ["ruby_buildpack"], + "buildpacks": [ + "ruby_buildpack" + ], "stack": "cflinuxfs2" } }, diff --git a/test/fixtures/v3/apps/POST_{id}_actions_start_response.json b/test/fixtures/v3/apps/POST_{id}_actions_start_response.json index 4d4880f..af1dfc7 100644 --- a/test/fixtures/v3/apps/POST_{id}_actions_start_response.json +++ b/test/fixtures/v3/apps/POST_{id}_actions_start_response.json @@ -7,7 +7,9 @@ "lifecycle": { "type": "buildpack", "data": { - "buildpacks": ["java_buildpack"], + "buildpacks": [ + "java_buildpack" + ], "stack": "cflinuxfs2" } }, diff --git a/test/fixtures/v3/buildpacks/GET_{id}_response.json b/test/fixtures/v3/buildpacks/GET_{id}_response.json index dae324d..72d5b16 100644 --- a/test/fixtures/v3/buildpacks/GET_{id}_response.json +++ b/test/fixtures/v3/buildpacks/GET_{id}_response.json @@ -10,16 +10,16 @@ "enabled": true, "locked": false, "metadata": { - "labels": { }, - "annotations": { } + "labels": {}, + "annotations": {} }, "links": { "self": { "href": "https://api.example.org/v3/buildpacks/fd35633f-5c5c-4e4e-a5a9-0722c970a9d2" }, "upload": { - "href": "https://api.example.org/v3/buildpacks/fd35633f-5c5c-4e4e-a5a9-0722c970a9d2/upload", - "method": "POST" + "href": "https://api.example.org/v3/buildpacks/fd35633f-5c5c-4e4e-a5a9-0722c970a9d2/upload", + "method": "POST" } } } \ No newline at end of file diff --git a/test/fixtures/v3/buildpacks/PATCH_{id}_response.json b/test/fixtures/v3/buildpacks/PATCH_{id}_response.json index 658a888..9b57f3c 100644 --- a/test/fixtures/v3/buildpacks/PATCH_{id}_response.json +++ b/test/fixtures/v3/buildpacks/PATCH_{id}_response.json @@ -10,16 +10,16 @@ "enabled": true, "locked": false, "metadata": { - "labels": { }, - "annotations": { } + "labels": {}, + "annotations": {} }, "links": { "self": { "href": "https://api.example.org/v3/buildpacks/fd35633f-5c5c-4e4e-a5a9-0722c970a9d2" }, "upload": { - "href": "https://api.example.org/v3/buildpacks/fd35633f-5c5c-4e4e-a5a9-0722c970a9d2/upload", - "method": "POST" + "href": "https://api.example.org/v3/buildpacks/fd35633f-5c5c-4e4e-a5a9-0722c970a9d2/upload", + "method": "POST" } } } diff --git a/test/fixtures/v3/buildpacks/POST_response.json b/test/fixtures/v3/buildpacks/POST_response.json index dae324d..72d5b16 100644 --- a/test/fixtures/v3/buildpacks/POST_response.json +++ b/test/fixtures/v3/buildpacks/POST_response.json @@ -10,16 +10,16 @@ "enabled": true, "locked": false, "metadata": { - "labels": { }, - "annotations": { } + "labels": {}, + "annotations": {} }, "links": { "self": { "href": "https://api.example.org/v3/buildpacks/fd35633f-5c5c-4e4e-a5a9-0722c970a9d2" }, "upload": { - "href": "https://api.example.org/v3/buildpacks/fd35633f-5c5c-4e4e-a5a9-0722c970a9d2/upload", - "method": "POST" + "href": "https://api.example.org/v3/buildpacks/fd35633f-5c5c-4e4e-a5a9-0722c970a9d2/upload", + "method": "POST" } } } \ No newline at end of file diff --git a/test/fixtures/v3/domains/GET_{id}_response.json b/test/fixtures/v3/domains/GET_{id}_response.json index c8c5e7a..91eb241 100644 --- a/test/fixtures/v3/domains/GET_{id}_response.json +++ b/test/fixtures/v3/domains/GET_{id}_response.json @@ -5,17 +5,23 @@ "name": "test-domain.com", "internal": false, "metadata": { - "labels": { }, - "annotations": { } + "labels": {}, + "annotations": {} }, "relationships": { "organization": { - "data": { "guid": "3a3f3d89-3f89-4f05-8188-751b298c79d5" } + "data": { + "guid": "3a3f3d89-3f89-4f05-8188-751b298c79d5" + } }, "shared_organizations": { "data": [ - {"guid": "404f3d89-3f89-6z72-8188-751b298d88d5"}, - {"guid": "416d3d89-3f89-8h67-2189-123b298d3592"} + { + "guid": "404f3d89-3f89-6z72-8188-751b298d88d5" + }, + { + "guid": "416d3d89-3f89-8h67-2189-123b298d3592" + } ] } }, diff --git a/test/fixtures/v3/domains/PATCH_{id}_response.json b/test/fixtures/v3/domains/PATCH_{id}_response.json index a762ad3..9942b22 100644 --- a/test/fixtures/v3/domains/PATCH_{id}_response.json +++ b/test/fixtures/v3/domains/PATCH_{id}_response.json @@ -5,17 +5,27 @@ "name": "test-domain.com", "internal": false, "metadata": { - "labels": { "key": "value" }, - "annotations": { "note": "detailed information" } + "labels": { + "key": "value" + }, + "annotations": { + "note": "detailed information" + } }, "relationships": { "organization": { - "data": { "guid": "3a3f3d89-3f89-4f05-8188-751b298c79d5" } + "data": { + "guid": "3a3f3d89-3f89-4f05-8188-751b298c79d5" + } }, "shared_organizations": { "data": [ - {"guid": "404f3d89-3f89-6z72-8188-751b298d88d5"}, - {"guid": "416d3d89-3f89-8h67-2189-123b298d3592"} + { + "guid": "404f3d89-3f89-6z72-8188-751b298d88d5" + }, + { + "guid": "416d3d89-3f89-8h67-2189-123b298d3592" + } ] } }, diff --git a/test/fixtures/v3/domains/POST_response.json b/test/fixtures/v3/domains/POST_response.json index c8c5e7a..91eb241 100644 --- a/test/fixtures/v3/domains/POST_response.json +++ b/test/fixtures/v3/domains/POST_response.json @@ -5,17 +5,23 @@ "name": "test-domain.com", "internal": false, "metadata": { - "labels": { }, - "annotations": { } + "labels": {}, + "annotations": {} }, "relationships": { "organization": { - "data": { "guid": "3a3f3d89-3f89-4f05-8188-751b298c79d5" } + "data": { + "guid": "3a3f3d89-3f89-4f05-8188-751b298c79d5" + } }, "shared_organizations": { "data": [ - {"guid": "404f3d89-3f89-6z72-8188-751b298d88d5"}, - {"guid": "416d3d89-3f89-8h67-2189-123b298d3592"} + { + "guid": "404f3d89-3f89-6z72-8188-751b298d88d5" + }, + { + "guid": "416d3d89-3f89-8h67-2189-123b298d3592" + } ] } }, diff --git a/test/fixtures/v3/domains/POST_{id}_relationships_shared_organizations_response.json b/test/fixtures/v3/domains/POST_{id}_relationships_shared_organizations_response.json index 670f5b2..5c8631b 100644 --- a/test/fixtures/v3/domains/POST_{id}_relationships_shared_organizations_response.json +++ b/test/fixtures/v3/domains/POST_{id}_relationships_shared_organizations_response.json @@ -1,6 +1,10 @@ { "data": [ - {"guid": "404f3d89-3f89-6z72-8188-751b298d88d5"}, - {"guid": "416d3d89-3f89-8h67-2189-123b298d3592"} + { + "guid": "404f3d89-3f89-6z72-8188-751b298d88d5" + }, + { + "guid": "416d3d89-3f89-8h67-2189-123b298d3592" + } ] } diff --git a/test/fixtures/v3/organizations/GET_{id}_default_domain_response.json b/test/fixtures/v3/organizations/GET_{id}_default_domain_response.json index c8c5e7a..91eb241 100644 --- a/test/fixtures/v3/organizations/GET_{id}_default_domain_response.json +++ b/test/fixtures/v3/organizations/GET_{id}_default_domain_response.json @@ -5,17 +5,23 @@ "name": "test-domain.com", "internal": false, "metadata": { - "labels": { }, - "annotations": { } + "labels": {}, + "annotations": {} }, "relationships": { "organization": { - "data": { "guid": "3a3f3d89-3f89-4f05-8188-751b298c79d5" } + "data": { + "guid": "3a3f3d89-3f89-4f05-8188-751b298c79d5" + } }, "shared_organizations": { "data": [ - {"guid": "404f3d89-3f89-6z72-8188-751b298d88d5"}, - {"guid": "416d3d89-3f89-8h67-2189-123b298d3592"} + { + "guid": "404f3d89-3f89-6z72-8188-751b298d88d5" + }, + { + "guid": "416d3d89-3f89-8h67-2189-123b298d3592" + } ] } }, diff --git a/test/fixtures/v3/service_instances/GET_response.json b/test/fixtures/v3/service_instances/GET_response.json index 708ef6f..8ef7d21 100644 --- a/test/fixtures/v3/service_instances/GET_response.json +++ b/test/fixtures/v3/service_instances/GET_response.json @@ -25,8 +25,8 @@ } }, "metadata": { - "labels": { }, - "annotations": { } + "labels": {}, + "annotations": {} }, "links": { "space": { diff --git a/test/fixtures/v3/service_instances/GET_{id}_response.json b/test/fixtures/v3/service_instances/GET_{id}_response.json index 3608a86..9c1e643 100644 --- a/test/fixtures/v3/service_instances/GET_{id}_response.json +++ b/test/fixtures/v3/service_instances/GET_{id}_response.json @@ -11,8 +11,8 @@ } }, "metadata": { - "labels": { }, - "annotations": { } + "labels": {}, + "annotations": {} }, "links": { "space": { diff --git a/test/networking/v1/external/test_policies.py b/test/networking/v1/external/test_policies.py index cd5565d..2dad659 100644 --- a/test/networking/v1/external/test_policies.py +++ b/test/networking/v1/external/test_policies.py @@ -3,7 +3,6 @@ from abstract_test_case import AbstractTestCase from cloudfoundry_client.networking.v1.external.policies import Policy -from fake_requests import mock_response class TestPolicies(unittest.TestCase, AbstractTestCase): @@ -15,10 +14,11 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = mock_response('/networking/v1/external/policies', - HTTPStatus.OK, - None, - 'networking', 'v1', 'external', 'policies', 'GET_response.json') + self.client.get.return_value = self.mock_response('/networking/v1/external/policies', + HTTPStatus.OK, + None, + 'networking', 'v1', 'external', 'policies', + 'GET_response.json') all_policies = [policy for policy in self.client.networking_v1_external.policies.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(2, len(all_policies)) @@ -26,9 +26,9 @@ def test_list(self): self.assertEqual(all_policies[0]['destination']['id'], "38f08df0-19df-4439-b4e9-61096d4301ea") def test_delete(self): - self.client.delete.return_value = mock_response('/networking/v1/external/policies/delete', - HTTPStatus.OK, - None) + self.client.delete.return_value = self.mock_response('/networking/v1/external/policies/delete', + HTTPStatus.OK, + None) policy = Policy(src_id='1081ceac-f5c4-47a8-95e8-88e1e302efb5', dst_id='38f08df0-19df-4439-b4e9-61096d4301ea', proto='tcp', @@ -36,11 +36,11 @@ def test_delete(self): end_port=1234) self.client.networking_v1_external.policies.delete([policy, ]) self.client.delete.assert_called_with({'policies': [ - {'source': - {'id': '1081ceac-f5c4-47a8-95e8-88e1e302efb5'}, - 'destination': { - 'id': '38f08df0-19df-4439-b4e9-61096d4301ea', - 'ports': {'start': 1234, 'end': 1234}, - 'protocol': 'tcp'} - } - ]}) + {'source': + {'id': '1081ceac-f5c4-47a8-95e8-88e1e302efb5'}, + 'destination': { + 'id': '38f08df0-19df-4439-b4e9-61096d4301ea', + 'ports': {'start': 1234, 'end': 1234}, + 'protocol': 'tcp'} + } + ]}) diff --git a/test/operations/push/test_push.py b/test/operations/push/test_push.py index f4eeca3..9c77127 100644 --- a/test/operations/push/test_push.py +++ b/test/operations/push/test_push.py @@ -5,7 +5,6 @@ import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase from cloudfoundry_client.operations.push.push import PushOperation -from fake_requests import get_fixtures_path class TestPushOperation(TestCase, AbstractTestCase): @@ -44,8 +43,9 @@ def test_to_host_should_remove_unwanted_characters(self): host = PushOperation._to_host('idzone-3.0.7-rec-tb1_bobby') self.assertEquals('idzone-307-rec-tb1-bobby', host) - @patch.object(sys, 'argv', ['main', 'push_app', get_fixtures_path('fake', 'manifest_main.yml'), '-space_guid', - 'space_id']) + @patch.object(sys, 'argv', + ['main', 'push_app', AbstractTestCase.get_fixtures_path('fake', 'manifest_main.yml'), '-space_guid', + 'space_id']) def test_main_push(self): class FakeOperation(object): def __init__(self): @@ -57,4 +57,4 @@ def __init__(self): new=lambda: client), \ patch('cloudfoundry_client.main.operation_commands.PushOperation', new=lambda c: push_operation): main.main() - push_operation.push.assert_called_with('space_id', get_fixtures_path('fake', 'manifest_main.yml')) + push_operation.push.assert_called_with('space_id', self.get_fixtures_path('fake', 'manifest_main.yml')) diff --git a/test/operations/push/validation/test_manifest_reader.py b/test/operations/push/validation/test_manifest_reader.py index 0ab60c0..0e9e0fc 100644 --- a/test/operations/push/validation/test_manifest_reader.py +++ b/test/operations/push/validation/test_manifest_reader.py @@ -98,4 +98,4 @@ def test_memory_in_mb(self): def test_memory_in_gb(self): manifest = dict(memory='1G') ManifestReader._convert_memory(manifest) - self.assertEqual(1024, manifest['memory']) \ No newline at end of file + self.assertEqual(1024, manifest['memory']) diff --git a/test/test_client.py b/test/test_client.py index 5c1af8e..b602815 100644 --- a/test/test_client.py +++ b/test/test_client.py @@ -7,7 +7,6 @@ from abstract_test_case import AbstractTestCase from cloudfoundry_client.client import CloudFoundryClient from fake_requests import MockResponse, MockSession, FakeRequests -from fake_requests import TARGET_ENDPOINT class TestCloudfoundryClient(unittest.TestCase, AbstractTestCase, ): @@ -21,15 +20,12 @@ def test_grant_password_request_with_token_format_opaque(self): with patch('oauth2_client.credentials_manager.requests', new=requests), \ patch('cloudfoundry_client.client.requests', new=requests): requests.Session.return_value = session - requests.get.return_value = MockResponse('%s/v2/info' % TARGET_ENDPOINT, - status_code=HTTPStatus.OK, - text=json.dumps(dict(api_version='2.1', - authorization_endpoint=TARGET_ENDPOINT))) - requests.post.return_value = MockResponse('%s/oauth/token' % TARGET_ENDPOINT, + self._mock_info_calls(requests) + requests.post.return_value = MockResponse('%s/oauth/token' % self.TOKEN_ENDPOINT, status_code=HTTPStatus.OK, text=json.dumps(dict(access_token='access-token', refresh_token='refresh-token'))) - client = CloudFoundryClient(TARGET_ENDPOINT, token_format='opaque') + client = CloudFoundryClient(self.TARGET_ENDPOINT, token_format='opaque') client.init_with_user_credentials('somebody', 'p@s$w0rd') self.assertEqual('Bearer access-token', session.headers.get('Authorization')) requests.post.assert_called_with(requests.post.return_value.url, @@ -48,15 +44,12 @@ def test_refresh_request_with_token_format_opaque(self): with patch('oauth2_client.credentials_manager.requests', new=requests), \ patch('cloudfoundry_client.client.requests', new=requests): requests.Session.return_value = session - requests.get.return_value = MockResponse('%s/v2/info' % TARGET_ENDPOINT, - status_code=HTTPStatus.OK, - text=json.dumps(dict(api_version='2.1', - authorization_endpoint=TARGET_ENDPOINT))) - requests.post.return_value = MockResponse('%s/oauth/token' % TARGET_ENDPOINT, + self._mock_info_calls(requests) + requests.post.return_value = MockResponse('%s/oauth/token' % self.TOKEN_ENDPOINT, status_code=HTTPStatus.OK, text=json.dumps(dict(access_token='access-token', refresh_token='refresh-token'))) - client = CloudFoundryClient(TARGET_ENDPOINT, token_format='opaque') + client = CloudFoundryClient(self.TARGET_ENDPOINT, token_format='opaque') client.init_with_token('refresh-token') self.assertEqual('Bearer access-token', session.headers.get('Authorization')) requests.post.assert_called_with(requests.post.return_value.url, @@ -74,16 +67,13 @@ def test_grant_password_request_with_login_hint(self): with patch('oauth2_client.credentials_manager.requests', new=requests), \ patch('cloudfoundry_client.client.requests', new=requests): requests.Session.return_value = session - requests.get.return_value = MockResponse('%s/v2/info' % TARGET_ENDPOINT, - status_code=HTTPStatus.OK, - text=json.dumps(dict(api_version='2.1', - authorization_endpoint=TARGET_ENDPOINT))) - requests.post.return_value = MockResponse('%s/oauth/token' % TARGET_ENDPOINT, + self._mock_info_calls(requests) + requests.post.return_value = MockResponse('%s/oauth/token' % self.TOKEN_ENDPOINT, status_code=HTTPStatus.OK, text=json.dumps(dict(access_token='access-token', refresh_token='refresh-token'))) - client = CloudFoundryClient(TARGET_ENDPOINT, login_hint=quote(json.dumps(dict(origin='uaa'), - separators=(',', ':')))) + client = CloudFoundryClient(self.TARGET_ENDPOINT, login_hint=quote(json.dumps(dict(origin='uaa'), + separators=(',', ':')))) client.init_with_user_credentials('somebody', 'p@s$w0rd') self.assertEqual('Bearer access-token', session.headers.get('Authorization')) requests.post.assert_called_with(requests.post.return_value.url, @@ -95,3 +85,16 @@ def test_grant_password_request_with_login_hint(self): headers=dict(Accept='application/json', Authorization='Basic Y2Y6'), proxies=dict(http='', https=''), verify=True) + + def test_get_info(self): + requests = FakeRequests() + session = MockSession() + with patch('oauth2_client.credentials_manager.requests', new=requests), \ + patch('cloudfoundry_client.client.requests', new=requests): + requests.Session.return_value = session + self._mock_info_calls(requests) + info = CloudFoundryClient._get_info(self.TARGET_ENDPOINT) + self.assertEqual(info.api_endpoint, self.TARGET_ENDPOINT) + self.assertEqual(info.api_v2_version, self.API_V2_VERSION) + self.assertEqual(info.doppler_endpoint, self.DOPPLER_ENDPOINT) + self.assertEqual(info.log_stream_endpoint, self.LOG_STREAM_ENDPOINT) diff --git a/test/v2/test_apps.py b/test/v2/test_apps.py index 5b9f6fc..91ef838 100644 --- a/test/v2/test_apps.py +++ b/test/v2/test_apps.py @@ -7,7 +7,6 @@ import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase from cloudfoundry_client.errors import InvalidStatusCode -from fake_requests import mock_response, TARGET_ENDPOINT class TestApps(unittest.TestCase, AbstractTestCase): @@ -19,10 +18,10 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = mock_response('/v2/apps', - HTTPStatus.OK, - None, - 'v2', 'apps', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v2/apps', + HTTPStatus.OK, + None, + 'v2', 'apps', 'GET_response.json') all_applications = [application for application in self.client.v2.apps.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(len(all_applications), 3) @@ -30,7 +29,7 @@ def test_list(self): self.assertEqual(all_applications[0]['entity']['name'], "name-423") def test_list_filtered(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/apps?q=name%3Aapplication_name&results-per-page=1&q=space_guid%3Aspace_guid', HTTPStatus.OK, None, @@ -40,7 +39,7 @@ def test_list_filtered(self): self.assertIsNotNone(application) def test_get_env(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/apps/app_id/env', HTTPStatus.OK, None, @@ -50,7 +49,7 @@ def test_get_env(self): self.assertIsNotNone(application) def test_get_instances(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/apps/app_id/instances', HTTPStatus.OK, None, @@ -60,7 +59,7 @@ def test_get_instances(self): self.assertIsNotNone(application) def test_get_stats(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/apps/app_id/stats', HTTPStatus.OK, None, @@ -70,13 +69,13 @@ def test_get_stats(self): self.assertIsNotNone(application) def test_associate_route(self): - self.client.put.return_value = mock_response('/v2/apps/app_id/routes/route_id', HTTPStatus.CREATED, None, - 'v2', 'apps', 'PUT_{id}_routes_{route_id}_response.json') + self.client.put.return_value = self.mock_response('/v2/apps/app_id/routes/route_id', HTTPStatus.CREATED, None, + 'v2', 'apps', 'PUT_{id}_routes_{route_id}_response.json') self.client.v2.apps.associate_route('app_id', 'route_id') self.client.put.assert_called_with(self.client.put.return_value.url, json=None) def test_list_routes(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/apps/app_id/routes?q=route_guid%3Aroute_id', HTTPStatus.OK, None, @@ -89,12 +88,13 @@ def test_list_routes(self): self.assertEqual(cpt, 1) def test_remove_route(self): - self.client.delete.return_value = mock_response('/v2/apps/app_id/routes/route_id', HTTPStatus.NO_CONTENT, None) + self.client.delete.return_value = self.mock_response('/v2/apps/app_id/routes/route_id', HTTPStatus.NO_CONTENT, + None) self.client.v2.apps.remove_route('app_id', 'route_id') self.client.delete.assert_called_with(self.client.delete.return_value.url) def test_list_service_bindings(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/apps/app_id/service_bindings', HTTPStatus.OK, None, @@ -105,7 +105,7 @@ def test_list_service_bindings(self): self.assertEqual(cpt, 1) def test_get_sumary(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/apps/app_id/summary', HTTPStatus.OK, None, @@ -116,7 +116,7 @@ def test_get_sumary(self): self.assertIsNotNone(application) def test_get(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/apps/app_id', HTTPStatus.OK, None, @@ -126,22 +126,22 @@ def test_get(self): self.assertIsNotNone(application) def test_start(self): - self.client.put.return_value = mock_response( + self.client.put.return_value = self.mock_response( '/v2/apps/app_id', HTTPStatus.CREATED, None, 'v2', 'apps', 'PUT_{id}_response.json') - mock_summary = mock_response( + mock_summary = self.mock_response( '/v2/apps/app_id/summary', HTTPStatus.OK, None, 'v2', 'apps', 'GET_{id}_summary_response.json') - mock_instances_stopped = mock_response( + mock_instances_stopped = self.mock_response( '/v2/apps/app_id/instances', HTTPStatus.BAD_REQUEST, None, 'v2', 'apps', 'GET_{id}_instances_stopped_response.json') - mock_instances_started = mock_response( + mock_instances_started = self.mock_response( '/v2/apps/app_id/instances', HTTPStatus.OK, None, @@ -160,7 +160,7 @@ def test_start(self): self.assertIsNotNone(application) def test_stop(self): - self.client.put.return_value = mock_response( + self.client.put.return_value = self.mock_response( '/v2/apps/app_id', HTTPStatus.CREATED, None, @@ -169,18 +169,18 @@ def test_stop(self): application = self.client.v2.apps.stop('app_id') self.client.put.assert_called_with(self.client.put.return_value.url, json=dict(state='STOPPED')) - self.client.get.assert_called_with('%s/v2/apps/app_id/instances' % TARGET_ENDPOINT) + self.client.get.assert_called_with('%s/v2/apps/app_id/instances' % self.TARGET_ENDPOINT) self.assertIsNotNone(application) def test_restart_instance(self): - self.client.delete.return_value = mock_response( + self.client.delete.return_value = self.mock_response( '/v2/apps/app_id/instances/666', HTTPStatus.NO_CONTENT, None) self.client.v2.apps.restart_instance('app_id', 666) self.client.delete.assert_called_with(self.client.delete.return_value.url) def test_create(self): - self.client.post.return_value = mock_response( + self.client.post.return_value = self.mock_response( '/v2/apps', HTTPStatus.CREATED, None, @@ -196,7 +196,7 @@ def test_create(self): self.assertIsNotNone(application) def test_update(self): - self.client.put.return_value = mock_response( + self.client.put.return_value = self.mock_response( '/v2/apps/app_id', HTTPStatus.CREATED, None, @@ -210,14 +210,14 @@ def test_update(self): self.assertIsNotNone(application) def test_remove(self): - self.client.delete.return_value = mock_response( + self.client.delete.return_value = self.mock_response( '/v2/apps/app_id', HTTPStatus.NO_CONTENT, None) self.client.v2.apps.remove('app_id') self.client.delete.assert_called_with(self.client.delete.return_value.url) def test_restage(self): - self.client.post.return_value = mock_response( + self.client.post.return_value = self.mock_response( '/v2/apps/app_id/restage', HTTPStatus.CREATED, None, @@ -227,18 +227,18 @@ def test_restage(self): def test_entity(self): self.client.get.side_effect = [ - mock_response( + self.mock_response( '/v2/apps/app_id', HTTPStatus.OK, None, 'v2', 'apps', 'GET_{id}_response.json'), - mock_response( + self.mock_response( '/v2/spaces/space_id', HTTPStatus.OK, None, 'v2', 'spaces', 'GET_{id}_response.json') , - mock_response( + self.mock_response( '/v2/routes', HTTPStatus.OK, None, @@ -256,10 +256,10 @@ def test_entity(self): def test_main_list_apps(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.get.return_value = mock_response('/v2/apps', - HTTPStatus.OK, - None, - 'v2', 'apps', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v2/apps', + HTTPStatus.OK, + None, + 'v2', 'apps', 'GET_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) @@ -267,9 +267,9 @@ def test_main_list_apps(self): def test_main_delete_apps(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.delete.return_value = mock_response('/v2/apps/906775ea-622e-4bc7-af5d-9aab3b652f81', - HTTPStatus.NO_CONTENT, - None) + self.client.delete.return_value = self.mock_response('/v2/apps/906775ea-622e-4bc7-af5d-9aab3b652f81', + HTTPStatus.NO_CONTENT, + None) main.main() self.client.delete.assert_called_with(self.client.delete.return_value.url) @@ -277,10 +277,10 @@ def test_main_delete_apps(self): def test_main_get_app(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.get.return_value = mock_response('/v2/apps/906775ea-622e-4bc7-af5d-9aab3b652f81', - HTTPStatus.OK, - None, - 'v2', 'apps', 'GET_{id}_response.json') + self.client.get.return_value = self.mock_response('/v2/apps/906775ea-622e-4bc7-af5d-9aab3b652f81', + HTTPStatus.OK, + None, + 'v2', 'apps', 'GET_{id}_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) @@ -288,10 +288,10 @@ def test_main_get_app(self): def test_main_restage_app(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.post.return_value = mock_response('/v2/apps/906775ea-622e-4bc7-af5d-9aab3b652f81/restage', - HTTPStatus.CREATED, - None, - 'v2', 'apps', 'POST_{id}_restage_response.json') + self.client.post.return_value = self.mock_response('/v2/apps/906775ea-622e-4bc7-af5d-9aab3b652f81/restage', + HTTPStatus.CREATED, + None, + 'v2', 'apps', 'POST_{id}_restage_response.json') main.main() self.client.post.assert_called_with(self.client.post.return_value.url, json=None) @@ -299,7 +299,7 @@ def test_main_restage_app(self): def test_main_restart_app_instance(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.delete.return_value = mock_response( + self.client.delete.return_value = self.mock_response( '/v2/apps/906775ea-622e-4bc7-af5d-9aab3b652f81/instances/666', HTTPStatus.NO_CONTENT, None) diff --git a/test/v2/test_buildpacks.py b/test/v2/test_buildpacks.py index ce01678..7ea4032 100644 --- a/test/v2/test_buildpacks.py +++ b/test/v2/test_buildpacks.py @@ -3,7 +3,6 @@ from http import HTTPStatus from abstract_test_case import AbstractTestCase -from fake_requests import mock_response class TestBuildpacks(unittest.TestCase, AbstractTestCase): @@ -15,16 +14,16 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = mock_response('/v2/buildpacks', - HTTPStatus.OK, - None, - 'v2', 'buildpacks', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v2/buildpacks', + HTTPStatus.OK, + None, + 'v2', 'buildpacks', 'GET_response.json') cpt = reduce(lambda increment, _: increment + 1, self.client.v2.buildpacks.list(), 0) self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(cpt, 3) def test_get(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/buildpacks/buildpack_id', HTTPStatus.OK, None, @@ -34,7 +33,7 @@ def test_get(self): self.assertIsNotNone(result) def test_update(self): - self.client.put.return_value = mock_response( + self.client.put.return_value = self.mock_response( '/v2/buildpacks/build_pack_id', HTTPStatus.CREATED, None, diff --git a/test/v2/test_doppler.py b/test/v2/test_doppler.py index 3324534..3c2e88f 100644 --- a/test/v2/test_doppler.py +++ b/test/v2/test_doppler.py @@ -5,7 +5,6 @@ from abstract_test_case import AbstractTestCase from cloudfoundry_client.doppler.client import DopplerClient -from fake_requests import mock_response, TARGET_ENDPOINT class TestLoggregator(unittest.TestCase, AbstractTestCase): @@ -15,7 +14,7 @@ def setUpClass(cls): def setUp(self): self.build_client() - self.doppler = DopplerClient(re.sub('^http', 'ws', TARGET_ENDPOINT), + self.doppler = DopplerClient(re.sub('^http', 'ws', self.TARGET_ENDPOINT), proxy='', verify_ssl=False, credentials_manager=self.client) @@ -23,11 +22,11 @@ def setUp(self): def test_recents(self): boundary = 'd661b2c1426a3abcf1c0524d7fdbc774c42a767bdd6702141702d16047bc' app_guid = 'app_id' - self.client.get.return_value = mock_response('/apps/%s/recentlogs' % app_guid, - HTTPStatus.OK, - {'content-type': - 'multipart/x-protobuf; boundary=%s' % boundary}, - 'recents', 'GET_response.bin') + self.client.get.return_value = self.mock_response('/apps/%s/recentlogs' % app_guid, + HTTPStatus.OK, + {'content-type': + 'multipart/x-protobuf; boundary=%s' % boundary}, + 'recents', 'GET_response.bin') cpt = reduce(lambda increment, _: increment + 1, self.doppler.recent_logs(app_guid), 0) self.client.get.assert_called_with(self.client.get.return_value.url, stream=True) self.assertEqual(cpt, 200) diff --git a/test/v2/test_entities.py b/test/v2/test_entities.py index 612b3e0..9494436 100644 --- a/test/v2/test_entities.py +++ b/test/v2/test_entities.py @@ -3,18 +3,18 @@ from http import HTTPStatus from unittest.mock import MagicMock, call +from abstract_test_case import AbstractTestCase from cloudfoundry_client.errors import InvalidEntity from cloudfoundry_client.v2.entities import EntityManager -from fake_requests import TARGET_ENDPOINT, mock_response -class TestEntities(unittest.TestCase): +class TestEntities(unittest.TestCase, AbstractTestCase): def test_invalid_entity_without_entity_attribute(self): client = MagicMock() - entity_manager = EntityManager(TARGET_ENDPOINT, client, '/fake/anyone') + entity_manager = EntityManager(self.TARGET_ENDPOINT, client, '/fake/anyone') - client.get.return_value = mock_response( + client.get.return_value = self.mock_response( '/fake/anyone/any-id', HTTPStatus.OK, None, @@ -24,9 +24,9 @@ def test_invalid_entity_without_entity_attribute(self): def test_invalid_entity_with_null_entity(self): client = MagicMock() - entity_manager = EntityManager(TARGET_ENDPOINT, client, '/fake/anyone') + entity_manager = EntityManager(self.TARGET_ENDPOINT, client, '/fake/anyone') - client.get.return_value = mock_response( + client.get.return_value = self.mock_response( '/fake/anyone/any-id', HTTPStatus.OK, None, @@ -36,9 +36,9 @@ def test_invalid_entity_with_null_entity(self): def test_invalid_entity_with_invalid_entity_type(self): client = MagicMock() - entity_manager = EntityManager(TARGET_ENDPOINT, client, '/fake/anyone') + entity_manager = EntityManager(self.TARGET_ENDPOINT, client, '/fake/anyone') - client.get.return_value = mock_response( + client.get.return_value = self.mock_response( '/fake/anyone/any-id', HTTPStatus.OK, None, @@ -80,17 +80,17 @@ def test_multi_query(self): def test_list(self): client = MagicMock() - entity_manager = EntityManager(TARGET_ENDPOINT, client, '/fake/first') + entity_manager = EntityManager(self.TARGET_ENDPOINT, client, '/fake/first') - first_response = mock_response( + first_response = self.mock_response( '/fake/first?order-direction=asc&page=1&results-per-page=20&q=space_guid%3Asome-id', HTTPStatus.OK, None, 'fake', 'GET_multi_page_0_response.json') - second_response = mock_response('/fake/next?order-direction=asc&page=2&results-per-page=50', - HTTPStatus.OK, - None, - 'fake', 'GET_multi_page_1_response.json') + second_response = self.mock_response('/fake/next?order-direction=asc&page=2&results-per-page=50', + HTTPStatus.OK, + None, + 'fake', 'GET_multi_page_1_response.json') client.get.side_effect = [first_response, second_response] cpt = reduce(lambda increment, _: increment + 1, entity_manager.list(**{"results-per-page": 20, @@ -104,9 +104,9 @@ def test_list(self): def test_iter(self): client = MagicMock() - entity_manager = EntityManager(TARGET_ENDPOINT, client, '/fake/something') + entity_manager = EntityManager(self.TARGET_ENDPOINT, client, '/fake/something') - client.get.return_value = mock_response( + client.get.return_value = self.mock_response( '/fake/something', HTTPStatus.OK, None, @@ -118,9 +118,9 @@ def test_iter(self): def test_get_elem(self): client = MagicMock() - entity_manager = EntityManager(TARGET_ENDPOINT, client, '/fake/something') + entity_manager = EntityManager(self.TARGET_ENDPOINT, client, '/fake/something') - client.get.return_value = mock_response( + client.get.return_value = self.mock_response( '/fake/something/with-id', HTTPStatus.OK, None, diff --git a/test/v2/test_events.py b/test/v2/test_events.py index ffac1ff..8d61889 100644 --- a/test/v2/test_events.py +++ b/test/v2/test_events.py @@ -2,7 +2,6 @@ from http import HTTPStatus from abstract_test_case import AbstractTestCase -from fake_requests import mock_response class TestEvents(unittest.TestCase, AbstractTestCase): @@ -14,10 +13,11 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = mock_response('/v2/events?q=type%3Aaudit.route.delete-request', - HTTPStatus.OK, - None, - 'v2', 'events', 'GET_response_audit.route.delete-request.json') + self.client.get.return_value = self.mock_response('/v2/events?q=type%3Aaudit.route.delete-request', + HTTPStatus.OK, + None, + 'v2', 'events', + 'GET_response_audit.route.delete-request.json') delete_route_events = [event for event in self.client.v2.event.list_by_type('audit.route.delete-request')] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(len(delete_route_events), 1) diff --git a/test/v2/test_organizations.py b/test/v2/test_organizations.py index 300e79b..efd8714 100644 --- a/test/v2/test_organizations.py +++ b/test/v2/test_organizations.py @@ -1,10 +1,9 @@ import unittest from functools import reduce from http import HTTPStatus -from unittest.mock import call, patch +from unittest.mock import call from abstract_test_case import AbstractTestCase -from fake_requests import mock_response class TestOrganizations(unittest.TestCase, AbstractTestCase): @@ -16,16 +15,16 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = mock_response('/v2/organizations?q=name%3Aorganization_name', - HTTPStatus.OK, - None, - 'v2', 'organizations', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v2/organizations?q=name%3Aorganization_name', + HTTPStatus.OK, + None, + 'v2', 'organizations', 'GET_response.json') cpt = reduce(lambda increment, _: increment + 1, self.client.v2.organizations.list(name='organization_name'), 0) self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(cpt, 1) def test_get(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/organizations/org_id', HTTPStatus.OK, None, @@ -36,12 +35,12 @@ def test_get(self): def test_entity(self): self.client.get.side_effect = [ - mock_response( + self.mock_response( '/v2/organizations/org_id', HTTPStatus.OK, None, 'v2', 'organizations', 'GET_{id}_response.json'), - mock_response( + self.mock_response( '/v2/organizations/fe79371b-39b8-4f0d-8331-cff423a06aca/spaces', HTTPStatus.OK, None, @@ -52,4 +51,3 @@ def test_entity(self): self.assertEqual(cpt, 1) self.client.get.assert_has_calls([call(side_effect.url) for side_effect in self.client.get.side_effect], any_order=False) - diff --git a/test/v2/test_routes.py b/test/v2/test_routes.py index 890da42..9b4706d 100644 --- a/test/v2/test_routes.py +++ b/test/v2/test_routes.py @@ -6,7 +6,6 @@ import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase -from fake_requests import mock_response class TestRoutes(unittest.TestCase, AbstractTestCase): @@ -18,18 +17,19 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/routes?q=organization_guid%3Aorganization_guid', HTTPStatus.OK, None, 'v2', 'routes', 'GET_response.json') - cpt = reduce(lambda increment, _: increment + 1, self.client.v2.routes.list(organization_guid='organization_guid'), + cpt = reduce(lambda increment, _: increment + 1, + self.client.v2.routes.list(organization_guid='organization_guid'), 0) self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(cpt, 1) def test_get(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/routes/route_id', HTTPStatus.OK, None, @@ -40,25 +40,25 @@ def test_get(self): def test_entity(self): self.client.get.side_effect = [ - mock_response( + self.mock_response( '/v2/routes/route_id', HTTPStatus.OK, None, 'v2', 'routes', 'GET_{id}_response.json'), - mock_response( + self.mock_response( '/v2/service_instances/e3db4ea8-ab0c-4c47-adf8-a70a8e990ee4', HTTPStatus.OK, None, 'v2', 'service_instances', 'GET_{id}_response.json'), - mock_response( + self.mock_response( '/v2/spaces/b3f94ab9-1520-478b-a6d6-eb467c179ada', HTTPStatus.OK, None, 'v2', 'spaces', 'GET_{id}_response.json'), - mock_response('/v2/routes/75c16cfe-9b8a-4faf-bb65-02c713c7956f/apps', - HTTPStatus.OK, - None, - 'v2', 'apps', 'GET_response.json') + self.mock_response('/v2/routes/75c16cfe-9b8a-4faf-bb65-02c713c7956f/apps', + HTTPStatus.OK, + None, + 'v2', 'apps', 'GET_response.json') ] route = self.client.v2.routes.get('route_id') self.assertIsNotNone(route.service_instance()) @@ -72,10 +72,10 @@ def test_entity(self): def test_main_list_routes(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.get.return_value = mock_response('/v2/routes', - HTTPStatus.OK, - None, - 'v2', 'routes', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v2/routes', + HTTPStatus.OK, + None, + 'v2', 'routes', 'GET_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) @@ -83,9 +83,9 @@ def test_main_list_routes(self): def test_main_get_route(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.get.return_value = mock_response('/v2/routes/75c16cfe-9b8a-4faf-bb65-02c713c7956f', - HTTPStatus.OK, - None, - 'v2', 'routes', 'GET_{id}_response.json') + self.client.get.return_value = self.mock_response('/v2/routes/75c16cfe-9b8a-4faf-bb65-02c713c7956f', + HTTPStatus.OK, + None, + 'v2', 'routes', 'GET_{id}_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) diff --git a/test/v2/test_service_bindings.py b/test/v2/test_service_bindings.py index 73a8903..79c63cd 100644 --- a/test/v2/test_service_bindings.py +++ b/test/v2/test_service_bindings.py @@ -6,7 +6,6 @@ import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase -from fake_requests import mock_response class TestServiceBindings(unittest.TestCase, AbstractTestCase): @@ -18,7 +17,7 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/service_bindings?q=service_instance_guid%3Ainstance_guid', HTTPStatus.OK, None, @@ -29,7 +28,7 @@ def test_list(self): self.assertEqual(cpt, 1) def test_get(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/service_bindings/service_binding_id', HTTPStatus.OK, None, @@ -39,13 +38,13 @@ def test_get(self): self.assertIsNotNone(result) def test_create(self): - self.client.post.return_value = mock_response( + self.client.post.return_value = self.mock_response( '/v2/service_bindings', HTTPStatus.CREATED, None, 'v2', 'service_bindings', 'POST_response.json') service_binding = self.client.v2.service_bindings.create('app_guid', 'instance_guid', - dict(the_service_broker='wants this object'), + dict(the_service_broker='wants this object'), 'binding_name') self.client.post.assert_called_with(self.client.post.return_value.url, json=dict(app_guid='app_guid', @@ -56,7 +55,7 @@ def test_create(self): self.assertIsNotNone(service_binding) def test_delete(self): - self.client.delete.return_value = mock_response( + self.client.delete.return_value = self.mock_response( '/v2/service_bindings/binding_id', HTTPStatus.NO_CONTENT, None) @@ -65,17 +64,17 @@ def test_delete(self): def test_entity(self): self.client.get.side_effect = [ - mock_response( + self.mock_response( '/v2/service_bindings/service_binding_id', HTTPStatus.OK, None, 'v2', 'service_bindings', 'GET_{id}_response.json'), - mock_response( + self.mock_response( '/v2/service_instances/ef0bf611-82c6-4603-99fc-3a1a893109d0', HTTPStatus.OK, None, 'v2', 'service_instances', 'GET_{id}_response.json'), - mock_response( + self.mock_response( '/v2/apps/c77953c8-6c35-46c7-816e-cf0c42ac2f52', HTTPStatus.OK, None, @@ -91,10 +90,10 @@ def test_entity(self): def test_main_list_service_bindings(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.get.return_value = mock_response('/v2/service_bindings', - HTTPStatus.OK, - None, - 'v2', 'service_bindings', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v2/service_bindings', + HTTPStatus.OK, + None, + 'v2', 'service_bindings', 'GET_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) @@ -102,9 +101,10 @@ def test_main_list_service_bindings(self): def test_main_get_service_binding(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.get.return_value = mock_response('/v2/service_bindings/eaabd042-8f5c-44a2-9580-1e114c36bdcb', - HTTPStatus.OK, - None, - 'v2', 'service_bindings', 'GET_{id}_response.json') + self.client.get.return_value = self.mock_response( + '/v2/service_bindings/eaabd042-8f5c-44a2-9580-1e114c36bdcb', + HTTPStatus.OK, + None, + 'v2', 'service_bindings', 'GET_{id}_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) diff --git a/test/v2/test_service_brokers.py b/test/v2/test_service_brokers.py index 9082622..23bb683 100644 --- a/test/v2/test_service_brokers.py +++ b/test/v2/test_service_brokers.py @@ -6,7 +6,6 @@ import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase -from fake_requests import mock_response class TestServiceBrokers(unittest.TestCase, AbstractTestCase): @@ -18,7 +17,7 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/service_brokers?q=space_guid%3Aspace_guid', HTTPStatus.OK, None, @@ -29,7 +28,7 @@ def test_list(self): self.assertEqual(cpt, 1) def test_get(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/service_brokers/broker_id', HTTPStatus.OK, None, @@ -39,7 +38,7 @@ def test_get(self): self.assertIsNotNone(result) def test_create(self): - self.client.post.return_value = mock_response( + self.client.post.return_value = self.mock_response( '/v2/service_brokers', HTTPStatus.CREATED, None, @@ -53,15 +52,15 @@ def test_create(self): self.assertIsNotNone(service_broker) def test_update(self): - self.client.put.return_value = mock_response( + self.client.put.return_value = self.mock_response( '/v2/service_brokers/broker_id', HTTPStatus.OK, None, 'v2', 'service_brokers', 'PUT_{id}_response.json') service_broker = self.client.v2.service_brokers.update('broker_id', - broker_url='new-url', - auth_username='new-username', - auth_password='P@sswd2') + broker_url='new-url', + auth_username='new-username', + auth_password='P@sswd2') self.client.put.assert_called_with(self.client.put.return_value.url, json=dict(broker_url='new-url', auth_username='new-username', @@ -69,7 +68,7 @@ def test_update(self): self.assertIsNotNone(service_broker) def test_delete(self): - self.client.delete.return_value = mock_response( + self.client.delete.return_value = self.mock_response( '/v2/service_brokers/broker_id', HTTPStatus.NO_CONTENT, None) @@ -79,21 +78,22 @@ def test_delete(self): @patch.object(sys, 'argv', ['main', 'list_service_brokers']) def test_main_list_service_brokers(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = mock_response('/v2/service_brokers', - HTTPStatus.OK, - None, - 'v2', 'service_brokers', 'GET_response.json') + new=lambda: self.client): + self.client.get.return_value = self.mock_response('/v2/service_brokers', + HTTPStatus.OK, + None, + 'v2', 'service_brokers', 'GET_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) @patch.object(sys, 'argv', ['main', 'get_service_broker', 'ade9730c-4ee5-4290-ad37-0b15cecd2ca6']) def test_main_get_service_broker(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = mock_response('/v2/service_brokers/ade9730c-4ee5-4290-ad37-0b15cecd2ca6', - HTTPStatus.OK, - None, - 'v2', 'service_brokers', 'GET_{id}_response.json') + new=lambda: self.client): + self.client.get.return_value = self.mock_response( + '/v2/service_brokers/ade9730c-4ee5-4290-ad37-0b15cecd2ca6', + HTTPStatus.OK, + None, + 'v2', 'service_brokers', 'GET_{id}_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) diff --git a/test/v2/test_service_instances.py b/test/v2/test_service_instances.py index bc74e25..870b1d4 100644 --- a/test/v2/test_service_instances.py +++ b/test/v2/test_service_instances.py @@ -6,7 +6,6 @@ import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase -from fake_requests import mock_response class TestServiceInstances(unittest.TestCase, AbstractTestCase): @@ -18,7 +17,7 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/service_instances?q=service_plan_guid%3Aplan_id&q=space_guid%3Aspace_guid', HTTPStatus.OK, None, @@ -29,7 +28,7 @@ def test_list(self): self.assertEqual(cpt, 1) def test_get(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/service_instances/instance_id', HTTPStatus.OK, None, @@ -39,7 +38,7 @@ def test_get(self): self.assertIsNotNone(result) def test_create(self): - self.client.post.return_value = mock_response( + self.client.post.return_value = self.mock_response( '/v2/service_instances', HTTPStatus.CREATED, None, @@ -61,7 +60,7 @@ def test_create(self): self.assertIsNotNone(service_instance) def test_update(self): - self.client.put.return_value = mock_response( + self.client.put.return_value = self.mock_response( '/v2/service_instances/instance_id', HTTPStatus.OK, None, @@ -74,7 +73,7 @@ def test_update(self): self.assertIsNotNone(service_instance) def test_update(self): - self.client.put.return_value = mock_response( + self.client.put.return_value = self.mock_response( '/v2/service_instances/instance_id', HTTPStatus.OK, None, @@ -87,7 +86,7 @@ def test_update(self): self.assertIsNotNone(service_instance) def test_delete_accepts_incomplete(self): - self.client.delete.return_value = mock_response( + self.client.delete.return_value = self.mock_response( '/v2/service_instances/instance_id', HTTPStatus.NO_CONTENT, None) @@ -95,7 +94,7 @@ def test_delete_accepts_incomplete(self): self.client.delete.assert_called_with(self.client.delete.return_value.url, params=dict(accepts_incomplete="true")) - self.client.delete.return_value = mock_response( + self.client.delete.return_value = self.mock_response( '/v2/service_instances/instance_id', HTTPStatus.NO_CONTENT, None) @@ -104,7 +103,7 @@ def test_delete_accepts_incomplete(self): params=dict(accepts_incomplete="true")) def test_delete_purge(self): - self.client.delete.return_value = mock_response( + self.client.delete.return_value = self.mock_response( '/v2/service_instances/instance_id', HTTPStatus.NO_CONTENT, None) @@ -112,7 +111,7 @@ def test_delete_purge(self): self.client.delete.assert_called_with(self.client.delete.return_value.url, params=dict(accepts_incomplete='true', purge="true")) - self.client.delete.return_value = mock_response( + self.client.delete.return_value = self.mock_response( '/v2/service_instances/instance_id', HTTPStatus.NO_CONTENT, None) @@ -120,7 +119,7 @@ def test_delete_purge(self): self.client.delete.assert_called_with(self.client.delete.return_value.url, params=dict(purge="true")) - self.client.delete.return_value = mock_response( + self.client.delete.return_value = self.mock_response( '/v2/service_instances/instance_id', HTTPStatus.NO_CONTENT, None) @@ -129,7 +128,7 @@ def test_delete_purge(self): params=dict(purge="true")) def test_delete(self): - self.client.delete.return_value = mock_response( + self.client.delete.return_value = self.mock_response( '/v2/service_instances/instance_id', HTTPStatus.NO_CONTENT, None) @@ -139,34 +138,34 @@ def test_delete(self): def test_entity(self): self.client.get.side_effect = [ - mock_response( + self.mock_response( '/v2/service_instances/instance_id', HTTPStatus.OK, None, 'v2', 'service_instances', 'GET_{id}_response.json'), - mock_response( + self.mock_response( '/v2/spaces/e3138257-8035-4c03-8aba-ab5d35eec0f9', HTTPStatus.OK, None, 'v2', 'spaces', 'GET_{id}_response.json') , - mock_response( + self.mock_response( '/v2/service_instances/df52420f-d5b9-4b86-a7d3-6d7005d1ce96/service_bindings', HTTPStatus.OK, None, 'v2', 'service_bindings', 'GET_response.json'), - mock_response( + self.mock_response( '/v2/service_plans/65740f84-214a-46cf-b8e3-2233d580f293', HTTPStatus.OK, None, 'v2', 'service_plans', 'GET_{id}_response.json'), - mock_response( + self.mock_response( '/v2/service_instances/df52420f-d5b9-4b86-a7d3-6d7005d1ce96/routes', HTTPStatus.OK, None, 'v2', 'routes', 'GET_response.json' ), - mock_response( + self.mock_response( '/v2/service_instances/df52420f-d5b9-4b86-a7d3-6d7005d1ce96/service_keys', HTTPStatus.OK, None, @@ -190,10 +189,10 @@ def test_entity(self): def test_main_list_service_instances(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.get.return_value = mock_response('/v2/service_instances', - HTTPStatus.OK, - None, - 'v2', 'service_instances', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v2/service_instances', + HTTPStatus.OK, + None, + 'v2', 'service_instances', 'GET_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) @@ -201,9 +200,10 @@ def test_main_list_service_instances(self): def test_main_get_service_instance(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.get.return_value = mock_response('/v2/service_instances/df52420f-d5b9-4b86-a7d3-6d7005d1ce96', - HTTPStatus.OK, - None, - 'v2', 'service_instances', 'GET_{id}_response.json') + self.client.get.return_value = self.mock_response( + '/v2/service_instances/df52420f-d5b9-4b86-a7d3-6d7005d1ce96', + HTTPStatus.OK, + None, + 'v2', 'service_instances', 'GET_{id}_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) diff --git a/test/v2/test_service_keys.py b/test/v2/test_service_keys.py index 79c4177..cb28c92 100644 --- a/test/v2/test_service_keys.py +++ b/test/v2/test_service_keys.py @@ -7,7 +7,6 @@ import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase -from fake_requests import mock_response class TestServiceKeys(unittest.TestCase, AbstractTestCase): @@ -19,7 +18,7 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/service_keys?q=service_instance_guid%3Ainstance_guid', HTTPStatus.OK, None, @@ -30,7 +29,7 @@ def test_list(self): self.assertEqual(cpt, 1) def test_get(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/service_keys/key_id', HTTPStatus.OK, None, @@ -40,7 +39,7 @@ def test_get(self): self.assertIsNotNone(result) def test_create(self): - self.client.post.return_value = mock_response( + self.client.post.return_value = self.mock_response( '/v2/service_keys', HTTPStatus.CREATED, None, @@ -53,7 +52,7 @@ def test_create(self): self.assertIsNotNone(service_key) def test_delete(self): - self.client.delete.return_value = mock_response( + self.client.delete.return_value = self.mock_response( '/v2/service_keys/key_id', HTTPStatus.NO_CONTENT, None) @@ -64,10 +63,10 @@ def test_delete(self): def test_main_list_service_keys(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.get.return_value = mock_response('/v2/service_keys', - HTTPStatus.OK, - None, - 'v2', 'service_keys', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v2/service_keys', + HTTPStatus.OK, + None, + 'v2', 'service_keys', 'GET_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) @@ -75,10 +74,10 @@ def test_main_list_service_keys(self): def test_main_get_service_key(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.get.return_value = mock_response('/v2/service_keys/67755c27-28ed-4087-9688-c07d92f3bcc9', - HTTPStatus.OK, - None, - 'v2', 'service_keys', 'GET_{id}_response.json') + self.client.get.return_value = self.mock_response('/v2/service_keys/67755c27-28ed-4087-9688-c07d92f3bcc9', + HTTPStatus.OK, + None, + 'v2', 'service_keys', 'GET_{id}_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) @@ -88,7 +87,7 @@ def test_main_get_service_key(self): def test_main_create_service_key(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.post.return_value = mock_response( + self.client.post.return_value = self.mock_response( '/v2/service_keys', HTTPStatus.CREATED, None, @@ -103,7 +102,7 @@ def test_main_create_service_key(self): def test_main_delete_service_key(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.delete.return_value = mock_response( + self.client.delete.return_value = self.mock_response( '/v2/service_keys/67755c27-28ed-4087-9688-c07d92f3bcc9', HTTPStatus.NO_CONTENT, None) diff --git a/test/v2/test_service_plan_visibilities.py b/test/v2/test_service_plan_visibilities.py index 2fa7f3b..a8532f9 100644 --- a/test/v2/test_service_plan_visibilities.py +++ b/test/v2/test_service_plan_visibilities.py @@ -7,7 +7,6 @@ import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase -from fake_requests import mock_response class TestServicePlanVisibilities(unittest.TestCase, AbstractTestCase): @@ -19,7 +18,7 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/service_plan_visibilities?q=space_guid%3Aspace_guid', HTTPStatus.OK, None, @@ -30,7 +29,7 @@ def test_list(self): self.assertEqual(cpt, 1) def test_get(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/service_plan_visibilities/guid', HTTPStatus.OK, None, @@ -40,7 +39,7 @@ def test_get(self): self.assertIsNotNone(result) def test_create(self): - self.client.post.return_value = mock_response( + self.client.post.return_value = self.mock_response( '/v2/service_plan_visibilities', HTTPStatus.CREATED, None, @@ -53,7 +52,7 @@ def test_create(self): self.assertIsNotNone(service_plan_visibilities) def test_update(self): - self.client.put.return_value = mock_response( + self.client.put.return_value = self.mock_response( '/v2/service_plan_visibilities/guid', HTTPStatus.OK, None, @@ -68,7 +67,7 @@ def test_update(self): self.assertIsNotNone(service_plan_visibilities) def test_delete(self): - self.client.delete.return_value = mock_response( + self.client.delete.return_value = self.mock_response( '/v2/service_plan_visibilities/guid', HTTPStatus.NO_CONTENT, None) @@ -81,7 +80,7 @@ def test_delete(self): def test_main_create_service_plan_visibility(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.post.return_value = mock_response( + self.client.post.return_value = self.mock_response( '/v2/service_plan_visibilities', HTTPStatus.CREATED, None, @@ -96,10 +95,10 @@ def test_main_create_service_plan_visibility(self): def test_main_list_service_plan_visibilities(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.get.return_value = mock_response('/v2/service_plan_visibilities', - HTTPStatus.OK, - None, - 'v2', 'service_plan_visibilities', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v2/service_plan_visibilities', + HTTPStatus.OK, + None, + 'v2', 'service_plan_visibilities', 'GET_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) @@ -107,7 +106,7 @@ def test_main_list_service_plan_visibilities(self): def test_main_get_service_plan_visibility(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/service_plan_visibilities/a353104b-1290-418c-bc03-0e647afd0853', HTTPStatus.OK, None, @@ -119,8 +118,9 @@ def test_main_get_service_plan_visibility(self): def test_main_delete_service_plan_visibility(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.delete.return_value = mock_response('/v2/service_plan_visibilities/906775ea-622e-4bc7-af5d-9aab3b652f81', - HTTPStatus.NO_CONTENT, - None) + self.client.delete.return_value = self.mock_response( + '/v2/service_plan_visibilities/906775ea-622e-4bc7-af5d-9aab3b652f81', + HTTPStatus.NO_CONTENT, + None) main.main() self.client.delete.assert_called_with(self.client.delete.return_value.url) diff --git a/test/v2/test_service_plans.py b/test/v2/test_service_plans.py index 2c1fcbd..a3820bb 100644 --- a/test/v2/test_service_plans.py +++ b/test/v2/test_service_plans.py @@ -6,7 +6,6 @@ import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase -from fake_requests import mock_response class TestServicePlans(unittest.TestCase, AbstractTestCase): @@ -18,17 +17,18 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/service_plans?q=service_guid%3Aservice_id', HTTPStatus.OK, None, 'v2', 'service_plans', 'GET_response.json') - cpt = reduce(lambda increment, _: increment + 1, self.client.v2.service_plans.list(service_guid='service_id'), 0) + cpt = reduce(lambda increment, _: increment + 1, self.client.v2.service_plans.list(service_guid='service_id'), + 0) self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(cpt, 1) def test_get(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/service_plans/plan_id', HTTPStatus.OK, None, @@ -38,7 +38,7 @@ def test_get(self): self.assertIsNotNone(result) def test_list_instances(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/service_plans/plan_id/service_instances?q=space_guid%3Aspace_id', HTTPStatus.OK, None, @@ -50,18 +50,18 @@ def test_list_instances(self): def test_entity(self): self.client.get.side_effect = [ - mock_response( + self.mock_response( '/v2/service_plans/plan_id', HTTPStatus.OK, None, 'v2', 'service_plans', 'GET_{id}_response.json'), - mock_response( + self.mock_response( '/v2/services/6a4abae6-93e0-438b-aaa2-5ae67f3a069d', HTTPStatus.OK, None, 'v2', 'services', 'GET_{id}_response.json') , - mock_response( + self.mock_response( '/v2/service_plans/5d8f3b0f-6b5b-487f-8fed-4c2d9b812a72/service_instances', HTTPStatus.OK, None, @@ -79,10 +79,10 @@ def test_entity(self): def test_main_list_service_plans(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.get.return_value = mock_response('/v2/service_plans', - HTTPStatus.OK, - None, - 'v2', 'service_plans', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v2/service_plans', + HTTPStatus.OK, + None, + 'v2', 'service_plans', 'GET_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) @@ -90,9 +90,9 @@ def test_main_list_service_plans(self): def test_main_get_service_plan(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.get.return_value = mock_response('/v2/service_plans/5d8f3b0f-6b5b-487f-8fed-4c2d9b812a72', - HTTPStatus.OK, - None, - 'v2', 'service_plans', 'GET_{id}_response.json') + self.client.get.return_value = self.mock_response('/v2/service_plans/5d8f3b0f-6b5b-487f-8fed-4c2d9b812a72', + HTTPStatus.OK, + None, + 'v2', 'service_plans', 'GET_{id}_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) diff --git a/test/v2/test_services.py b/test/v2/test_services.py index f81333b..a49b6d4 100644 --- a/test/v2/test_services.py +++ b/test/v2/test_services.py @@ -6,7 +6,6 @@ import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase -from fake_requests import mock_response class TestServices(unittest.TestCase, AbstractTestCase): @@ -18,16 +17,16 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = mock_response('/v2/services?q=label%3Asome_label', - HTTPStatus.OK, - None, - 'v2', 'services', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v2/services?q=label%3Asome_label', + HTTPStatus.OK, + None, + 'v2', 'services', 'GET_response.json') cpt = reduce(lambda increment, _: increment + 1, self.client.v2.services.list(label='some_label'), 0) self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(cpt, 1) def test_get(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/services/service_id', HTTPStatus.OK, None, @@ -38,12 +37,12 @@ def test_get(self): def test_entity(self): self.client.get.side_effect = [ - mock_response( + self.mock_response( '/v2/services/service_id', HTTPStatus.OK, None, 'v2', 'services', 'GET_{id}_response.json'), - mock_response( + self.mock_response( '/v2/services/2c883dbb-a726-4ecf-a0b7-d65588897e7f/service_plans', HTTPStatus.OK, None, @@ -59,21 +58,21 @@ def test_entity(self): @patch.object(sys, 'argv', ['main', 'list_services']) def test_main_list_services(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = mock_response('/v2/services', - HTTPStatus.OK, - None, - 'v2', 'services', 'GET_response.json') + new=lambda: self.client): + self.client.get.return_value = self.mock_response('/v2/services', + HTTPStatus.OK, + None, + 'v2', 'services', 'GET_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) @patch.object(sys, 'argv', ['main', 'get_service', '2c883dbb-a726-4ecf-a0b7-d65588897e7f']) def test_main_get_service(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = mock_response('/v2/services/2c883dbb-a726-4ecf-a0b7-d65588897e7f', - HTTPStatus.OK, - None, - 'v2', 'services', 'GET_{id}_response.json') + new=lambda: self.client): + self.client.get.return_value = self.mock_response('/v2/services/2c883dbb-a726-4ecf-a0b7-d65588897e7f', + HTTPStatus.OK, + None, + 'v2', 'services', 'GET_{id}_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) diff --git a/test/v2/test_spaces.py b/test/v2/test_spaces.py index c484834..6db230f 100644 --- a/test/v2/test_spaces.py +++ b/test/v2/test_spaces.py @@ -6,7 +6,6 @@ import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase -from fake_requests import mock_response class TestSpaces(unittest.TestCase, AbstractTestCase): @@ -18,16 +17,16 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = mock_response('/v2/spaces?q=organization_guid%3Aorg_id', - HTTPStatus.OK, - None, - 'v2', 'spaces', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v2/spaces?q=organization_guid%3Aorg_id', + HTTPStatus.OK, + None, + 'v2', 'spaces', 'GET_response.json') cpt = reduce(lambda increment, _: increment + 1, self.client.v2.spaces.list(organization_guid='org_id'), 0) self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(cpt, 1) def test_get(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v2/spaces/space_id', HTTPStatus.OK, None, @@ -38,22 +37,22 @@ def test_get(self): def test_entity(self): self.client.get.side_effect = [ - mock_response( + self.mock_response( '/v2/spaces/space_id', HTTPStatus.OK, None, 'v2', 'spaces', 'GET_{id}_response.json'), - mock_response( + self.mock_response( '/v2/organizations/d7d77408-a250-45e3-8de5-71fcf199bbab', HTTPStatus.OK, None, 'v2', 'organizations', 'GET_{id}_response.json'), - mock_response( + self.mock_response( '/v2/spaces/2d745a4b-67e3-4398-986e-2adbcf8f7ec9/apps', HTTPStatus.OK, None, 'v2', 'apps', 'GET_response.json'), - mock_response( + self.mock_response( '/v2/spaces/2d745a4b-67e3-4398-986e-2adbcf8f7ec9/service_instances', HTTPStatus.OK, None, @@ -72,10 +71,10 @@ def test_entity(self): def test_main_list_spaces(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.get.return_value = mock_response('/v2/spaces', - HTTPStatus.OK, - None, - 'v2', 'spaces', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v2/spaces', + HTTPStatus.OK, + None, + 'v2', 'spaces', 'GET_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) @@ -83,9 +82,9 @@ def test_main_list_spaces(self): def test_main_get_spaces(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.get.return_value = mock_response('/v2/spaces/2d745a4b-67e3-4398-986e-2adbcf8f7ec9', - HTTPStatus.OK, - None, - 'v2', 'spaces', 'GET_{id}_response.json') + self.client.get.return_value = self.mock_response('/v2/spaces/2d745a4b-67e3-4398-986e-2adbcf8f7ec9', + HTTPStatus.OK, + None, + 'v2', 'spaces', 'GET_{id}_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) diff --git a/test/v3/test_apps.py b/test/v3/test_apps.py index d57bcc4..28a23ca 100644 --- a/test/v3/test_apps.py +++ b/test/v3/test_apps.py @@ -3,7 +3,6 @@ from abstract_test_case import AbstractTestCase from cloudfoundry_client.v3.entities import Entity -from fake_requests import mock_response class TestApps(unittest.TestCase, AbstractTestCase): @@ -15,10 +14,10 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = mock_response('/v3/apps', - HTTPStatus.OK, - None, - 'v3', 'apps', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v3/apps', + HTTPStatus.OK, + None, + 'v3', 'apps', 'GET_response.json') all_applications = [application for application in self.client.v3.apps.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(2, len(all_applications)) @@ -26,19 +25,19 @@ def test_list(self): self.assertIsInstance(all_applications[0], Entity) def test_get(self): - self.client.get.return_value = mock_response('/v3/apps/app_id', - HTTPStatus.OK, - None, - 'v3', 'apps', 'GET_{id}_response.json') + self.client.get.return_value = self.mock_response('/v3/apps/app_id', + HTTPStatus.OK, + None, + 'v3', 'apps', 'GET_{id}_response.json') application = self.client.v3.apps.get('app_id') self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual("my_app", application['name']) self.assertIsInstance(application, Entity) def test_get_then_space(self): - get_app = mock_response('/v3/apps/app_id', HTTPStatus.OK, None, 'v3', 'apps', 'GET_{id}_response.json') - get_space = mock_response('/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576', HTTPStatus.OK, None, - 'v3', 'spaces', 'GET_{id}_response.json') + get_app = self.mock_response('/v3/apps/app_id', HTTPStatus.OK, None, 'v3', 'apps', 'GET_{id}_response.json') + get_space = self.mock_response('/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576', HTTPStatus.OK, None, + 'v3', 'spaces', 'GET_{id}_response.json') self.client.get.side_effect = [ get_app, get_space @@ -50,10 +49,10 @@ def test_get_then_space(self): self.assertEqual("my-space", space['name']) def test_get_then_start(self): - self.client.get.return_value = mock_response('/v3/apps/app_id', HTTPStatus.OK, None, - 'v3', 'apps', 'GET_{id}_response.json') - self.client.post.return_value = mock_response('/v3/apps/app_id/actions/start', HTTPStatus.OK, None, - 'v3', 'apps', 'POST_{id}_actions_start_response.json') + self.client.get.return_value = self.mock_response('/v3/apps/app_id', HTTPStatus.OK, None, + 'v3', 'apps', 'GET_{id}_response.json') + self.client.post.return_value = self.mock_response('/v3/apps/app_id/actions/start', HTTPStatus.OK, None, + 'v3', 'apps', 'POST_{id}_actions_start_response.json') app = self.client.v3.apps.get('app_id').start() self.client.get.assert_called_with(self.client.get.return_value.url) @@ -62,7 +61,7 @@ def test_get_then_start(self): self.assertIsInstance(app, Entity) def test_remove(self): - self.client.delete.return_value = mock_response( + self.client.delete.return_value = self.mock_response( '/v3/apps/app_id', HTTPStatus.NO_CONTENT, None) self.client.v3.apps.remove('app_id') self.client.delete.assert_called_with(self.client.delete.return_value.url) diff --git a/test/v3/test_buildpacks.py b/test/v3/test_buildpacks.py index bb9f540..585c0cd 100644 --- a/test/v3/test_buildpacks.py +++ b/test/v3/test_buildpacks.py @@ -6,7 +6,6 @@ import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase from cloudfoundry_client.v3.entities import Entity -from fake_requests import mock_response class TestBuildpacks(unittest.TestCase, AbstractTestCase): @@ -18,10 +17,10 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = mock_response('/v3/buildpacks', - HTTPStatus.OK, - None, - 'v3', 'buildpacks', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v3/buildpacks', + HTTPStatus.OK, + None, + 'v3', 'buildpacks', 'GET_response.json') all_buildpacks = [buildpack for buildpack in self.client.v3.buildpacks.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(1, len(all_buildpacks)) @@ -29,7 +28,7 @@ def test_list(self): self.assertIsInstance(all_buildpacks[0], Entity) def test_get(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v3/buildpacks/buildpack_id', HTTPStatus.OK, None, @@ -39,7 +38,7 @@ def test_get(self): self.assertIsNotNone(result) def test_update(self): - self.client.patch.return_value = mock_response( + self.client.patch.return_value = self.mock_response( '/v3/buildpacks/buildpack_id', HTTPStatus.OK, None, @@ -58,11 +57,11 @@ def test_update(self): 'labels': None, 'annotations': None } - }) + }) self.assertIsNotNone(result) def test_create(self): - self.client.post.return_value = mock_response( + self.client.post.return_value = self.mock_response( '/v3/buildpacks', HTTPStatus.OK, None, @@ -82,11 +81,11 @@ def test_create(self): 'labels': None, 'annotations': None } - }) + }) self.assertIsNotNone(result) def test_remove(self): - self.client.delete.return_value = mock_response( + self.client.delete.return_value = self.mock_response( '/v3/buildpacks/buildpack_id', HTTPStatus.NO_CONTENT, None) @@ -97,20 +96,20 @@ def test_remove(self): def test_main_list_buildpacks(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.get.return_value = mock_response('/v3/buildpacks', - HTTPStatus.OK, - None, - 'v3', 'buildpacks', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v3/buildpacks', + HTTPStatus.OK, + None, + 'v3', 'buildpacks', 'GET_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) @patch.object(sys, 'argv', ['main', 'get_buildpack', '6e72c33b-dff0-4020-8603-bcd8a4eb05e4']) def test_main_get_buildpack(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = mock_response('/v3/buildpacks/6e72c33b-dff0-4020-8603-bcd8a4eb05e4', - HTTPStatus.OK, - None, - 'v3', 'buildpacks', 'GET_{id}_response.json') + new=lambda: self.client): + self.client.get.return_value = self.mock_response('/v3/buildpacks/6e72c33b-dff0-4020-8603-bcd8a4eb05e4', + HTTPStatus.OK, + None, + 'v3', 'buildpacks', 'GET_{id}_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) diff --git a/test/v3/test_domains.py b/test/v3/test_domains.py index 61af12b..de3502d 100644 --- a/test/v3/test_domains.py +++ b/test/v3/test_domains.py @@ -7,7 +7,6 @@ from abstract_test_case import AbstractTestCase from cloudfoundry_client.v3.domains import Domain from cloudfoundry_client.v3.entities import Entity, ToManyRelationship, ToOneRelationship -from fake_requests import mock_response class TestDomains(unittest.TestCase, AbstractTestCase): @@ -19,10 +18,10 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = mock_response('/v3/domains', - HTTPStatus.OK, - None, - 'v3', 'domains', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v3/domains', + HTTPStatus.OK, + None, + 'v3', 'domains', 'GET_response.json') all_domains = [domain for domain in self.client.v3.domains.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(1, len(all_domains)) @@ -32,7 +31,7 @@ def test_list(self): self.assertIsInstance(domain, Domain) def test_get(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v3/domains/domain_id', HTTPStatus.OK, None, @@ -43,7 +42,7 @@ def test_get(self): self.assertIsInstance(result, Domain) def test_update(self): - self.client.patch.return_value = mock_response( + self.client.patch.return_value = self.mock_response( '/v3/domains/domain_id', HTTPStatus.OK, None, @@ -59,7 +58,7 @@ def test_update(self): self.assertIsInstance(result, Domain) def test_create(self): - self.client.post.return_value = mock_response( + self.client.post.return_value = self.mock_response( '/v3/domains', HTTPStatus.OK, None, @@ -89,7 +88,7 @@ def test_create(self): self.assertIsInstance(result, Domain) def test_remove(self): - self.client.delete.return_value = mock_response( + self.client.delete.return_value = self.mock_response( '/v3/domains/domain_id', HTTPStatus.NO_CONTENT, None) @@ -97,10 +96,10 @@ def test_remove(self): self.client.delete.assert_called_with(self.client.delete.return_value.url) def test_list_domains_for_org(self): - self.client.get.return_value = mock_response('/v3/organizations/org_id/domains', - HTTPStatus.OK, - None, - 'v3', 'domains', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v3/organizations/org_id/domains', + HTTPStatus.OK, + None, + 'v3', 'domains', 'GET_response.json') all_domains = [domain for domain in self.client.v3.domains.list_domains_for_org('org_id')] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(1, len(all_domains)) @@ -109,7 +108,7 @@ def test_list_domains_for_org(self): self.assertIsInstance(domain, Domain) def test_share_domain(self): - self.client.post.return_value = mock_response( + self.client.post.return_value = self.mock_response( '/v3/domains/domain_id/relationships/shared_organizations', HTTPStatus.CREATED, None, @@ -130,7 +129,7 @@ def test_share_domain(self): result.guids[1] = 'organization-guid-1' def test_unshare_domain(self): - self.client.delete.return_value = mock_response( + self.client.delete.return_value = self.mock_response( '/v3/domains/domain_id/relationships/shared_organizations/org_id', HTTPStatus.NO_CONTENT, None) @@ -141,10 +140,10 @@ def test_unshare_domain(self): def test_main_list_domains(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.get.return_value = mock_response('/v3/domains', - HTTPStatus.OK, - None, - 'v3', 'domains', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v3/domains', + HTTPStatus.OK, + None, + 'v3', 'domains', 'GET_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) @@ -152,9 +151,9 @@ def test_main_list_domains(self): def test_main_get_domain(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.get.return_value = mock_response('/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5', - HTTPStatus.OK, - None, - 'v3', 'domains', 'GET_{id}_response.json') + self.client.get.return_value = self.mock_response('/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5', + HTTPStatus.OK, + None, + 'v3', 'domains', 'GET_{id}_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) diff --git a/test/v3/test_feature_flags.py b/test/v3/test_feature_flags.py index b6ff46f..b2489d6 100644 --- a/test/v3/test_feature_flags.py +++ b/test/v3/test_feature_flags.py @@ -2,7 +2,6 @@ from http import HTTPStatus from abstract_test_case import AbstractTestCase -from fake_requests import mock_response class TestFeatureFlags(unittest.TestCase, AbstractTestCase): @@ -14,10 +13,10 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = mock_response('/v3/feature_flags', - HTTPStatus.OK, - None, - 'v3', 'feature_flags', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v3/feature_flags', + HTTPStatus.OK, + None, + 'v3', 'feature_flags', 'GET_response.json') all_feature_flags = [feature_flag for feature_flag in self.client.v3.feature_flags.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(2, len(all_feature_flags)) @@ -25,16 +24,16 @@ def test_list(self): self.assertEqual(all_feature_flags[1]['name'], "my_second_feature_flag") def test_get(self): - self.client.get.return_value = mock_response('/v3/feature_flags/feature_flag_name', - HTTPStatus.OK, - None, - 'v3', 'feature_flags', 'GET_{id}_response.json') + self.client.get.return_value = self.mock_response('/v3/feature_flags/feature_flag_name', + HTTPStatus.OK, + None, + 'v3', 'feature_flags', 'GET_{id}_response.json') feature_flag = self.client.v3.feature_flags.get('feature_flag_name') self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual("my_feature_flag", feature_flag['name']) def test_update(self): - self.client.patch.return_value = mock_response( + self.client.patch.return_value = self.mock_response( '/v3/feature_flags/feature_flag_name', HTTPStatus.OK, None, diff --git a/test/v3/test_isolation_segments.py b/test/v3/test_isolation_segments.py index 035aec9..aea2b04 100644 --- a/test/v3/test_isolation_segments.py +++ b/test/v3/test_isolation_segments.py @@ -3,7 +3,6 @@ from abstract_test_case import AbstractTestCase from cloudfoundry_client.v3.entities import Entity, ToManyRelationship -from fake_requests import mock_response class TestIsolationSegments(unittest.TestCase, AbstractTestCase): @@ -15,10 +14,10 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = mock_response('/v3/isolation_segments', - HTTPStatus.OK, - None, - 'v3', 'isolation_segments', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v3/isolation_segments', + HTTPStatus.OK, + None, + 'v3', 'isolation_segments', 'GET_response.json') all_isolation_segments = [isolation_segment for isolation_segment in self.client.v3.isolation_segments.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(5, len(all_isolation_segments)) @@ -28,7 +27,7 @@ def test_list(self): self.assertIsInstance(isolation_segment, Entity) def test_get(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v3/isolation_segments/isolation_segment_id', HTTPStatus.OK, None, @@ -39,7 +38,7 @@ def test_get(self): self.assertIsInstance(result, Entity) def test_update(self): - self.client.patch.return_value = mock_response( + self.client.patch.return_value = self.mock_response( '/v3/isolation_segments/isolation_segment_id', HTTPStatus.OK, None, @@ -51,15 +50,15 @@ def test_update(self): json={ 'name': 'new-name', 'metadata': { - 'labels': {'key': 'value'}, - 'annotations': None - } + 'labels': {'key': 'value'}, + 'annotations': None + } }) self.assertIsNotNone(result) self.assertIsInstance(result, Entity) def test_create(self): - self.client.post.return_value = mock_response( + self.client.post.return_value = self.mock_response( '/v3/isolation_segments', HTTPStatus.OK, None, @@ -80,7 +79,7 @@ def test_create(self): self.assertIsInstance(result, Entity) def test_remove(self): - self.client.delete.return_value = mock_response( + self.client.delete.return_value = self.mock_response( '/v3/isolation_segments/isolation_segment_id', HTTPStatus.NO_CONTENT, None) @@ -88,7 +87,7 @@ def test_remove(self): self.client.delete.assert_called_with(self.client.delete.return_value.url) def test_entitle_organizations(self): - self.client.post.return_value = mock_response( + self.client.post.return_value = self.mock_response( '/v3/isolation_segments/isolation_segment_id/relationships/organizations', HTTPStatus.OK, None, @@ -107,7 +106,7 @@ def test_entitle_organizations(self): self.assertIsNotNone(result['links']) def test_revoke_organization(self): - self.client.delete.return_value = mock_response( + self.client.delete.return_value = self.mock_response( '/v3/isolation_segments/isolation_segment_id/relationships/organizations/org_id', HTTPStatus.NO_CONTENT, None) @@ -115,7 +114,7 @@ def test_revoke_organization(self): self.client.delete.assert_called_with(self.client.delete.return_value.url) def test_list_entitled_organizations(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v3/isolation_segments/isolation_segment_id/relationships/organizations', HTTPStatus.OK, None, @@ -127,7 +126,7 @@ def test_list_entitled_organizations(self): self.assertIsNotNone(result['links']) def test_list_entitled_spaces(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v3/isolation_segments/isolation_segment_id/relationships/spaces', HTTPStatus.OK, None, @@ -137,4 +136,3 @@ def test_list_entitled_spaces(self): self.assertIsInstance(result, ToManyRelationship) self.assertEqual(2, len(result.guids)) self.assertIsNotNone(result['links']) - diff --git a/test/v3/test_organizations.py b/test/v3/test_organizations.py index d3e5bfe..9d04368 100644 --- a/test/v3/test_organizations.py +++ b/test/v3/test_organizations.py @@ -6,7 +6,6 @@ import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase from cloudfoundry_client.v3.entities import Entity, ToOneRelationship -from fake_requests import mock_response class TestOrganizations(unittest.TestCase, AbstractTestCase): @@ -18,10 +17,10 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = mock_response('/v3/organizations', - HTTPStatus.OK, - None, - 'v3', 'organizations', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v3/organizations', + HTTPStatus.OK, + None, + 'v3', 'organizations', 'GET_response.json') all_organizations = [organization for organization in self.client.v3.organizations.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(2, len(all_organizations)) @@ -29,17 +28,17 @@ def test_list(self): self.assertIsInstance(all_organizations[0], Entity) def test_get(self): - self.client.get.return_value = mock_response('/v3/organizations/organization_id', - HTTPStatus.OK, - None, - 'v3', 'organizations', 'GET_{id}_response.json') + self.client.get.return_value = self.mock_response('/v3/organizations/organization_id', + HTTPStatus.OK, + None, + 'v3', 'organizations', 'GET_{id}_response.json') organization = self.client.v3.organizations.get('organization_id') self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual("my-organization", organization['name']) self.assertIsInstance(organization, Entity) def test_update(self): - self.client.patch.return_value = mock_response( + self.client.patch.return_value = self.mock_response( '/v3/organizations/organization_id', HTTPStatus.OK, None, @@ -57,7 +56,7 @@ def test_update(self): self.assertIsNotNone(result) def test_create(self): - self.client.post.return_value = mock_response( + self.client.post.return_value = self.mock_response( '/v3/organizations', HTTPStatus.OK, None, @@ -76,7 +75,7 @@ def test_create(self): self.assertIsNotNone(result) def test_remove(self): - self.client.delete.return_value = mock_response( + self.client.delete.return_value = self.mock_response( '/v3/organizations/organization_id', HTTPStatus.NO_CONTENT, None) @@ -84,7 +83,7 @@ def test_remove(self): self.client.delete.assert_called_with(self.client.delete.return_value.url) def test_assign_default_isolation_segment(self): - self.client.patch.return_value = mock_response( + self.client.patch.return_value = self.mock_response( '/v3/organizations/organization_id/relationships/default_isolation_segment', HTTPStatus.OK, None, @@ -101,7 +100,7 @@ def test_assign_default_isolation_segment(self): self.assertEqual(result.guid, "9d8e007c-ce52-4ea7-8a57-f2825d2c6b39") def test_get_default_isolation_segment(self): - self.client.get.return_value = mock_response( + self.client.get.return_value = self.mock_response( '/v3/organizations/organization_id/relationships/default_isolation_segment', HTTPStatus.OK, None, @@ -115,20 +114,21 @@ def test_get_default_isolation_segment(self): self.assertEqual(result.guid, "9d8e007c-ce52-4ea7-8a57-f2825d2c6b39") def test_get_default_domain(self): - self.client.get.return_value = mock_response('/v3/organizations/organization_id/domains/default', - HTTPStatus.OK, - None, - 'v3', 'organizations', 'GET_{id}_default_domain_response.json') + self.client.get.return_value = self.mock_response('/v3/organizations/organization_id/domains/default', + HTTPStatus.OK, + None, + 'v3', 'organizations', + 'GET_{id}_default_domain_response.json') default_domain = self.client.v3.organizations.get_default_domain('organization_id') self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual("test-domain.com", default_domain['name']) self.assertIsInstance(default_domain, Entity) def test_get_usage_summary(self): - self.client.get.return_value = mock_response('/v3/organizations/organization_id/usage_summary', - HTTPStatus.OK, - None, - 'v3', 'organizations', 'GET_{id}_usage_summary_response.json') + self.client.get.return_value = self.mock_response('/v3/organizations/organization_id/usage_summary', + HTTPStatus.OK, + None, + 'v3', 'organizations', 'GET_{id}_usage_summary_response.json') self.client.v3.organizations.get_usage_summary('organization_id') self.client.get.assert_called_with(self.client.get.return_value.url) @@ -136,10 +136,10 @@ def test_get_usage_summary(self): def test_main_list_organizations(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.get.return_value = mock_response('/v3/organizations', - HTTPStatus.OK, - None, - 'v3', 'organizations', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v3/organizations', + HTTPStatus.OK, + None, + 'v3', 'organizations', 'GET_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) @@ -147,9 +147,9 @@ def test_main_list_organizations(self): def test_main_get_organizations(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.get.return_value = mock_response('/v3/organizations/24637893-3b77-489d-bb79-8466f0d88b52', - HTTPStatus.OK, - None, - 'v3', 'organizations', 'GET_{id}_response.json') + self.client.get.return_value = self.mock_response('/v3/organizations/24637893-3b77-489d-bb79-8466f0d88b52', + HTTPStatus.OK, + None, + 'v3', 'organizations', 'GET_{id}_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) diff --git a/test/v3/test_service_instances.py b/test/v3/test_service_instances.py index b6a58ed..b8e1eb7 100644 --- a/test/v3/test_service_instances.py +++ b/test/v3/test_service_instances.py @@ -3,7 +3,6 @@ from abstract_test_case import AbstractTestCase from cloudfoundry_client.v3.entities import Entity -from fake_requests import mock_response class TestServiceInstances(unittest.TestCase, AbstractTestCase): @@ -15,10 +14,10 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = mock_response('/v3/service_instances', - HTTPStatus.OK, - None, - 'v3', 'service_instances', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v3/service_instances', + HTTPStatus.OK, + None, + 'v3', 'service_instances', 'GET_response.json') all_service_instances = [service_instance for service_instance in self.client.v3.service_instances.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(1, len(all_service_instances)) @@ -26,13 +25,11 @@ def test_list(self): self.assertIsInstance(all_service_instances[0], Entity) def test_get(self): - self.client.get.return_value = mock_response('/v3/service_instances/service_instance_id', - HTTPStatus.OK, - None, - 'v3', 'service_instances', 'GET_{id}_response.json') + self.client.get.return_value = self.mock_response('/v3/service_instances/service_instance_id', + HTTPStatus.OK, + None, + 'v3', 'service_instances', 'GET_{id}_response.json') service_instance = self.client.v3.service_instances.get('service_instance_id') self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual("85ccdcad-d725-4109-bca4-fd6ba062b5c8", service_instance['guid']) self.assertIsInstance(service_instance, Entity) - - diff --git a/test/v3/test_spaces.py b/test/v3/test_spaces.py index 712481e..c6d8b2e 100644 --- a/test/v3/test_spaces.py +++ b/test/v3/test_spaces.py @@ -4,7 +4,6 @@ from abstract_test_case import AbstractTestCase from cloudfoundry_client.v3.entities import Entity, ToOneRelationship -from fake_requests import mock_response class TestSpaces(unittest.TestCase, AbstractTestCase): @@ -16,7 +15,7 @@ def setUp(self): self.build_client() def test_create(self): - self.client.post.return_value = mock_response( + self.client.post.return_value = self.mock_response( '/v3/spaces', HTTPStatus.OK, None, @@ -36,10 +35,10 @@ def test_create(self): self.assertIsInstance(result, Entity) def test_list(self): - self.client.get.return_value = mock_response('/v3/spaces', - HTTPStatus.OK, - None, - 'v3', 'spaces', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v3/spaces', + HTTPStatus.OK, + None, + 'v3', 'spaces', 'GET_response.json') all_spaces = [space for space in self.client.v3.spaces.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(2, len(all_spaces)) @@ -47,20 +46,21 @@ def test_list(self): self.assertIsInstance(all_spaces[0], Entity) def test_get(self): - self.client.get.return_value = mock_response('/v3/spaces/space_id', - HTTPStatus.OK, - None, - 'v3', 'spaces', 'GET_{id}_response.json') + self.client.get.return_value = self.mock_response('/v3/spaces/space_id', + HTTPStatus.OK, + None, + 'v3', 'spaces', 'GET_{id}_response.json') space = self.client.v3.spaces.get('space_id') self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual("my-space", space['name']) self.assertIsInstance(space, Entity) def test_get_then_organization(self): - get_space = mock_response('/v3/spaces/space_id', HTTPStatus.OK, None, - 'v3', 'spaces', 'GET_{id}_response.json') - get_organization = mock_response('/v3/organizations/e00705b9-7b42-4561-ae97-2520399d2133', HTTPStatus.OK, None, - 'v3', 'organizations', 'GET_{id}_response.json') + get_space = self.mock_response('/v3/spaces/space_id', HTTPStatus.OK, None, + 'v3', 'spaces', 'GET_{id}_response.json') + get_organization = self.mock_response('/v3/organizations/e00705b9-7b42-4561-ae97-2520399d2133', HTTPStatus.OK, + None, + 'v3', 'organizations', 'GET_{id}_response.json') self.client.get.side_effect = [ get_space, get_organization @@ -72,11 +72,11 @@ def test_get_then_organization(self): self.assertEqual("my-organization", organization['name']) def test_get_assigned_isolation_segment(self): - self.client.get.return_value = mock_response('/v3/spaces/space_id/relationships/isolation_segment', - HTTPStatus.OK, - None, - 'v3', 'spaces', - 'GET_{id}_relationships_isolation_segment_response.json') + self.client.get.return_value = self.mock_response('/v3/spaces/space_id/relationships/isolation_segment', + HTTPStatus.OK, + None, + 'v3', 'spaces', + 'GET_{id}_relationships_isolation_segment_response.json') result = self.client.v3.spaces.get_assigned_isolation_segment('space_id') @@ -84,11 +84,11 @@ def test_get_assigned_isolation_segment(self): self.assertEqual('e4c91047-3b29-4fda-b7f9-04033e5a9c9f', result.guid) def test_assign_isolation_segment(self): - self.client.patch.return_value = mock_response('/v3/spaces/space_id/relationships/isolation_segment', - HTTPStatus.OK, - None, - 'v3', 'spaces', - 'POST_{id}_relationships_isolation_segment_response.json') + self.client.patch.return_value = self.mock_response('/v3/spaces/space_id/relationships/isolation_segment', + HTTPStatus.OK, + None, + 'v3', 'spaces', + 'POST_{id}_relationships_isolation_segment_response.json') result = self.client.v3.spaces.assign_isolation_segment('space_id', 'iso-seg-guid') self.client.patch.assert_called_with(self.client.patch.return_value.url, json={ @@ -100,7 +100,7 @@ def test_assign_isolation_segment(self): self.assertEqual('iso-seg-guid', result.guid) def test_remove(self): - self.client.delete.return_value = mock_response( + self.client.delete.return_value = self.mock_response( '/v3/spaces/space_id', HTTPStatus.NO_CONTENT, None) diff --git a/test/v3/test_tasks.py b/test/v3/test_tasks.py index b457797..d1381b0 100644 --- a/test/v3/test_tasks.py +++ b/test/v3/test_tasks.py @@ -6,7 +6,6 @@ from abstract_test_case import AbstractTestCase from cloudfoundry_client.main import main from cloudfoundry_client.v3.entities import Entity -from fake_requests import mock_response class TestTasks(unittest.TestCase, AbstractTestCase): @@ -18,10 +17,10 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = mock_response('/v3/tasks', - HTTPStatus.OK, - None, - 'v3', 'tasks', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v3/tasks', + HTTPStatus.OK, + None, + 'v3', 'tasks', 'GET_response.json') all_tasks = [task for task in self.client.v3.tasks.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(2, len(all_tasks)) @@ -29,17 +28,17 @@ def test_list(self): self.assertIsInstance(all_tasks[0], Entity) def test_get(self): - self.client.get.return_value = mock_response('/v3/tasks/task_id', - HTTPStatus.OK, - None, - 'v3', 'tasks', 'GET_{id}_response.json') + self.client.get.return_value = self.mock_response('/v3/tasks/task_id', + HTTPStatus.OK, + None, + 'v3', 'tasks', 'GET_{id}_response.json') task = self.client.v3.tasks.get('task_id') self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual("migrate", task['name']) self.assertIsInstance(task, Entity) def test_create(self): - self.client.post.return_value = mock_response( + self.client.post.return_value = self.mock_response( '/v3/apps/app_guid/tasks', HTTPStatus.CREATED, None, @@ -51,7 +50,7 @@ def test_create(self): self.assertIsNotNone(task) def test_cancel(self): - self.client.post.return_value = mock_response( + self.client.post.return_value = self.mock_response( '/v3/tasks/task_guid/actions/cancel', HTTPStatus.ACCEPTED, None, @@ -64,10 +63,10 @@ def test_cancel(self): def test_list_tasks(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.get.return_value = mock_response('/v3/tasks?names=task_name', - HTTPStatus.OK, - None, - 'v3', 'tasks', 'GET_response.json') + self.client.get.return_value = self.mock_response('/v3/tasks?names=task_name', + HTTPStatus.OK, + None, + 'v3', 'tasks', 'GET_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) @@ -75,10 +74,10 @@ def test_list_tasks(self): def test_create_task(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.post.return_value = mock_response('/v3/apps/app_id/tasks', - HTTPStatus.CREATED, - None, - 'v3', 'tasks', 'POST_response.json') + self.client.post.return_value = self.mock_response('/v3/apps/app_id/tasks', + HTTPStatus.CREATED, + None, + 'v3', 'tasks', 'POST_response.json') main.main() self.client.post.assert_called_with(self.client.post.return_value.url, files=None, @@ -88,9 +87,9 @@ def test_create_task(self): def test_cancel_task(self): with patch('cloudfoundry_client.main.main.build_client_from_configuration', new=lambda: self.client): - self.client.post.return_value = mock_response('/v3/tasks/task_id/actions/cancel', - HTTPStatus.CREATED, - None, - 'v3', 'tasks', 'POST_{id}_actions_cancel_response.json') + self.client.post.return_value = self.mock_response('/v3/tasks/task_id/actions/cancel', + HTTPStatus.CREATED, + None, + 'v3', 'tasks', 'POST_{id}_actions_cancel_response.json') main.main() self.client.post.assert_called_with(self.client.post.return_value.url, files=None, json=None) From a7ecdd0c88814dbfe95e52e4a09e5c5890a337ae Mon Sep 17 00:00:00 2001 From: Alina Golovchenko Date: Wed, 18 Mar 2020 16:19:16 +0100 Subject: [PATCH 025/264] Async requests in context manager --- main/cloudfoundry_client/rlpgateway/client.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/main/cloudfoundry_client/rlpgateway/client.py b/main/cloudfoundry_client/rlpgateway/client.py index a5b201c..0b16a8b 100644 --- a/main/cloudfoundry_client/rlpgateway/client.py +++ b/main/cloudfoundry_client/rlpgateway/client.py @@ -23,20 +23,20 @@ def __init__(self, rlp_gateway_endpoint, proxy, verify_ssl, credentials_manager) if proxy is not None and len(proxy) > 0: proxy_domain = urlparse(proxy).netloc - idx = proxy_domain.find(':') + idx = proxy_domain.find(":") if 0 < idx < len(proxy_domain) - 2: self.proxy_host = proxy_domain[:idx] - self.proxy_port = int(proxy_domain[idx + 1:]) + self.proxy_port = int(proxy_domain[idx + 1 :]) async def stream_logs(self, app_guid): - url = '%s/v2/read?log&source_id=%s' % (self.rlp_gateway_endpoint, app_guid) + url = "%s/v2/read?log&source_id=%s" % (self.rlp_gateway_endpoint, app_guid) async with aiohttp.ClientSession() as session: - response = await session.get( + async with session.get( url=url, headers={"Authorization": self.credentials_manager._access_token}, - ) - if response.status == 204: - yield {} - else: - async for data in response.content.iter_any(): - yield data + ) as response: + if response.status == 204: + yield {} + else: + async for data in response.content.iter_any(): + yield data From c06909fd779eedff5f2d0bd36c78d4864fee9eb7 Mon Sep 17 00:00:00 2001 From: Alina Golovchenko Date: Thu, 19 Mar 2020 10:24:29 +0100 Subject: [PATCH 026/264] Fix readme for RLP logs --- README.rst | 11 +++++++++-- main/cloudfoundry_client/rlpgateway/client.py | 6 +++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 9dbdffb..142f359 100644 --- a/README.rst +++ b/README.rst @@ -286,16 +286,23 @@ Logs can also be streamed directly from RLP gateway: .. code-block:: python + import asyncio from cloudfoundry_client.client import CloudFoundryClient + target_endpoint = 'https://somewhere.org' proxy = dict(http=os.environ.get('HTTP_PROXY', ''), https=os.environ.get('HTTPS_PROXY', '')) rlp_client = CloudFoundryClient(target_endpoint, client_id='client_id', client_secret='client_secret', verify=False) # init with client credentials rlp_client.init_with_client_credentials() - async for log in rlp_client.rlpgateway.stream_logs('app-guid'): - print(log) + async def get_logs_for_app(rlp_client, app_guid): + async for log in rlp_client.rlpgateway.stream_logs(app_guid): + print(log) + loop = asyncio.get_event_loop() + loop.create_task(get_logs_for_app(rlp_client, "app_guid")) + loop.run_forever() + loop.close() Command Line Interface ---------------------- diff --git a/main/cloudfoundry_client/rlpgateway/client.py b/main/cloudfoundry_client/rlpgateway/client.py index 0b16a8b..54afcfd 100644 --- a/main/cloudfoundry_client/rlpgateway/client.py +++ b/main/cloudfoundry_client/rlpgateway/client.py @@ -23,13 +23,13 @@ def __init__(self, rlp_gateway_endpoint, proxy, verify_ssl, credentials_manager) if proxy is not None and len(proxy) > 0: proxy_domain = urlparse(proxy).netloc - idx = proxy_domain.find(":") + idx = proxy_domain.find(':') if 0 < idx < len(proxy_domain) - 2: self.proxy_host = proxy_domain[:idx] - self.proxy_port = int(proxy_domain[idx + 1 :]) + self.proxy_port = int(proxy_domain[idx + 1:]) async def stream_logs(self, app_guid): - url = "%s/v2/read?log&source_id=%s" % (self.rlp_gateway_endpoint, app_guid) + url = '%s/v2/read?log&source_id=%s' % (self.rlp_gateway_endpoint, app_guid) async with aiohttp.ClientSession() as session: async with session.get( url=url, From 3c8efe2495a212a45173f5de13d7eb4afba1bc32 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Thu, 19 Mar 2020 10:40:48 +0100 Subject: [PATCH 027/264] Prepare 1.12 release --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index a0a9265..1b57ebd 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.11.0" +__version__ = "1.12.0" From 9a23be1bae0af0cd067d3dab26ae747b930e5f39 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Thu, 19 Mar 2020 11:19:53 +0100 Subject: [PATCH 028/264] Fix README --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index 142f359..3bc16f8 100644 --- a/README.rst +++ b/README.rst @@ -71,6 +71,7 @@ To instantiate the client, nothing easier It can also be instantiated with oauth code flow if you possess a dedicated oauth application with its redirection .. code-block:: python + from flask import request from cloudfoundry_client.client import CloudFoundryClient target_endpoint = 'https://somewhere.org' From ab87489fff052f6dbb1c217548712af2b4233d9f Mon Sep 17 00:00:00 2001 From: Alina Golovchenko Date: Fri, 20 Mar 2020 15:56:56 +0100 Subject: [PATCH 029/264] Possibility to add custom params and headers to RLP client requests --- README.rst | 8 ++++-- main/cloudfoundry_client/rlpgateway/client.py | 25 +++++++++++-------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/README.rst b/README.rst index 142f359..98657a9 100644 --- a/README.rst +++ b/README.rst @@ -281,8 +281,9 @@ Logs can also be streamed using a websocket as follows: # read message infinitely (use break to exit... it will close the underlying websocket) print(log) +.. -Logs can also be streamed directly from RLP gateway: +Logs can also be streamed directly from RLP Gateway: .. code-block:: python @@ -296,13 +297,16 @@ Logs can also be streamed directly from RLP gateway: rlp_client.init_with_client_credentials() async def get_logs_for_app(rlp_client, app_guid): - async for log in rlp_client.rlpgateway.stream_logs(app_guid): + async for log in rlp_client.rlpgateway.stream_logs(app_guid, + params=['counter', 'gauge', 'counter.name=request_count'], + headers={'User-Agent': 'test'})): print(log) loop = asyncio.get_event_loop() loop.create_task(get_logs_for_app(rlp_client, "app_guid")) loop.run_forever() loop.close() +.. Command Line Interface ---------------------- diff --git a/main/cloudfoundry_client/rlpgateway/client.py b/main/cloudfoundry_client/rlpgateway/client.py index 54afcfd..2de1dfd 100644 --- a/main/cloudfoundry_client/rlpgateway/client.py +++ b/main/cloudfoundry_client/rlpgateway/client.py @@ -23,18 +23,23 @@ def __init__(self, rlp_gateway_endpoint, proxy, verify_ssl, credentials_manager) if proxy is not None and len(proxy) > 0: proxy_domain = urlparse(proxy).netloc - idx = proxy_domain.find(':') + idx = proxy_domain.find(":") if 0 < idx < len(proxy_domain) - 2: self.proxy_host = proxy_domain[:idx] - self.proxy_port = int(proxy_domain[idx + 1:]) - - async def stream_logs(self, app_guid): - url = '%s/v2/read?log&source_id=%s' % (self.rlp_gateway_endpoint, app_guid) - async with aiohttp.ClientSession() as session: - async with session.get( - url=url, - headers={"Authorization": self.credentials_manager._access_token}, - ) as response: + self.proxy_port = int(proxy_domain[idx + 1 :]) + + async def stream_logs(self, app_guid, **kwargs): + url = f"{self.rlp_gateway_endpoint}/v2/read?log&source_id={app_guid}" + headers = { + "Authorization": self.credentials_manager._access_token, + } + if "headers" in kwargs: + headers.update(kwargs["headers"]) + if "params" in kwargs: + for param in kwargs["params"]: + url = f"{url}&{param}" + async with aiohttp.ClientSession(headers=headers) as session: + async with session.get(url=url) as response: if response.status == 204: yield {} else: From 3587e31a750d8a20f40ea3770db9d1c026b70274 Mon Sep 17 00:00:00 2001 From: Alina Golovchenko Date: Fri, 20 Mar 2020 16:18:50 +0100 Subject: [PATCH 030/264] Formatting --- main/cloudfoundry_client/rlpgateway/client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/cloudfoundry_client/rlpgateway/client.py b/main/cloudfoundry_client/rlpgateway/client.py index 2de1dfd..0f05cdf 100644 --- a/main/cloudfoundry_client/rlpgateway/client.py +++ b/main/cloudfoundry_client/rlpgateway/client.py @@ -23,10 +23,10 @@ def __init__(self, rlp_gateway_endpoint, proxy, verify_ssl, credentials_manager) if proxy is not None and len(proxy) > 0: proxy_domain = urlparse(proxy).netloc - idx = proxy_domain.find(":") + idx = proxy_domain.find(':') if 0 < idx < len(proxy_domain) - 2: self.proxy_host = proxy_domain[:idx] - self.proxy_port = int(proxy_domain[idx + 1 :]) + self.proxy_port = int(proxy_domain[idx + 1:]) async def stream_logs(self, app_guid, **kwargs): url = f"{self.rlp_gateway_endpoint}/v2/read?log&source_id={app_guid}" From bbc97094c990e499713ec2b176d36df923f41ea3 Mon Sep 17 00:00:00 2001 From: Alina Golovchenko Date: Mon, 23 Mar 2020 09:31:19 +0100 Subject: [PATCH 031/264] User-agent --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 98657a9..4f0896a 100644 --- a/README.rst +++ b/README.rst @@ -299,7 +299,7 @@ Logs can also be streamed directly from RLP Gateway: async def get_logs_for_app(rlp_client, app_guid): async for log in rlp_client.rlpgateway.stream_logs(app_guid, params=['counter', 'gauge', 'counter.name=request_count'], - headers={'User-Agent': 'test'})): + headers={'User-Agent': 'cf-python-client'})): print(log) loop = asyncio.get_event_loop() From fbfd095d3759681ac2149c23bf002576a8a48ed6 Mon Sep 17 00:00:00 2001 From: Alina Golovchenko Date: Wed, 25 Mar 2020 10:42:25 +0100 Subject: [PATCH 032/264] Request params --- main/cloudfoundry_client/rlpgateway/client.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/main/cloudfoundry_client/rlpgateway/client.py b/main/cloudfoundry_client/rlpgateway/client.py index 0f05cdf..7c9dbd7 100644 --- a/main/cloudfoundry_client/rlpgateway/client.py +++ b/main/cloudfoundry_client/rlpgateway/client.py @@ -36,10 +36,11 @@ async def stream_logs(self, app_guid, **kwargs): if "headers" in kwargs: headers.update(kwargs["headers"]) if "params" in kwargs: - for param in kwargs["params"]: - url = f"{url}&{param}" + params = kwargs["params"] + else: + params = None async with aiohttp.ClientSession(headers=headers) as session: - async with session.get(url=url) as response: + async with session.get(url=url, params=params) as response: if response.status == 204: yield {} else: From c0cb1384577ec0153f2180dc1a0b4db420de6880 Mon Sep 17 00:00:00 2001 From: Alina Golovchenko Date: Wed, 25 Mar 2020 10:44:44 +0100 Subject: [PATCH 033/264] Request params in README --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 4f0896a..eba8447 100644 --- a/README.rst +++ b/README.rst @@ -298,7 +298,7 @@ Logs can also be streamed directly from RLP Gateway: async def get_logs_for_app(rlp_client, app_guid): async for log in rlp_client.rlpgateway.stream_logs(app_guid, - params=['counter', 'gauge', 'counter.name=request_count'], + params={'counter': '', 'gauge': ''}, headers={'User-Agent': 'cf-python-client'})): print(log) From fd71462cd1a179ab22eea2a6cfcde66cc2f2ec88 Mon Sep 17 00:00:00 2001 From: Alina Golovchenko Date: Fri, 27 Mar 2020 11:20:47 +0100 Subject: [PATCH 034/264] Request params RLP --- main/cloudfoundry_client/rlpgateway/client.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/main/cloudfoundry_client/rlpgateway/client.py b/main/cloudfoundry_client/rlpgateway/client.py index 7c9dbd7..3d6c413 100644 --- a/main/cloudfoundry_client/rlpgateway/client.py +++ b/main/cloudfoundry_client/rlpgateway/client.py @@ -23,22 +23,19 @@ def __init__(self, rlp_gateway_endpoint, proxy, verify_ssl, credentials_manager) if proxy is not None and len(proxy) > 0: proxy_domain = urlparse(proxy).netloc - idx = proxy_domain.find(':') + idx = proxy_domain.find(":") if 0 < idx < len(proxy_domain) - 2: self.proxy_host = proxy_domain[:idx] - self.proxy_port = int(proxy_domain[idx + 1:]) + self.proxy_port = int(proxy_domain[idx + 1 :]) async def stream_logs(self, app_guid, **kwargs): - url = f"{self.rlp_gateway_endpoint}/v2/read?log&source_id={app_guid}" - headers = { - "Authorization": self.credentials_manager._access_token, - } + url = f"{self.rlp_gateway_endpoint}/v2/read" + headers = dict(Authorization=self.credentials_manager._access_token) + params = dict(log="", source_id=app_guid) if "headers" in kwargs: headers.update(kwargs["headers"]) if "params" in kwargs: - params = kwargs["params"] - else: - params = None + params.update(kwargs["params"]) async with aiohttp.ClientSession(headers=headers) as session: async with session.get(url=url, params=params) as response: if response.status == 204: From 13aa82014c6527e35e7c6dbd29a0c761b2197032 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Fri, 27 Mar 2020 11:38:01 +0100 Subject: [PATCH 035/264] Prepare 1.12.1 release --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 1b57ebd..eff74ab 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.12.0" +__version__ = "1.12.1" From cadb4129a99d6aa98386386ecb7cef5e1730e8a4 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Wed, 1 Apr 2020 10:24:45 +0200 Subject: [PATCH 036/264] Fix client build when no dopple/rpl gateway info See #82 --- main/cloudfoundry_client/client.py | 18 +++++++++++------- test/abstract_test_case.py | 7 +++---- test/test_client.py | 20 ++++++++++++++++++++ 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 69b4ed6..92baa9d 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -41,8 +41,8 @@ def __init__(self, api_v2_version: str, authorization_endpoint: str, token_endpoint: str, api_endpoint: str, - doppler_endpoint: str, - log_stream_endpoint: str): + doppler_endpoint: Optional[str], + log_stream_endpoint: Optional[str]): self.api_v2_version = api_v2_version self.authorization_endpoint = authorization_endpoint self.token_endpoint = token_endpoint @@ -166,17 +166,21 @@ def _get_info(target_endpoint: str, proxy: Optional[dict] = None, verify: bool = http='', https=''), verify=verify)) info = info_response.json() - links_response = CloudFoundryClient._check_response(requests.get('%s/' % target_endpoint, + root_response = CloudFoundryClient._check_response(requests.get('%s/' % target_endpoint, proxies=proxy if proxy is not None else dict( http='', https=''), verify=verify)) - links = links_response.json() - return Info(links['links']['cloud_controller_v2']['meta']['version'], + root_info = root_response.json() + + root_links = root_info['links'] + logging = root_links.get('logging') + log_stream = root_links.get('log_stream') + return Info(root_links['cloud_controller_v2']['meta']['version'], info['authorization_endpoint'], info['token_endpoint'], target_endpoint, - links['links'].get('logging').get('href'), - links['links'].get('log_stream').get('href')) + logging.get('href') if logging is not None else None, + log_stream.get('href') if log_stream is not None else None) @staticmethod def _is_token_expired(response: Response) -> bool: diff --git a/test/abstract_test_case.py b/test/abstract_test_case.py index 818a68a..464b8dc 100644 --- a/test/abstract_test_case.py +++ b/test/abstract_test_case.py @@ -10,7 +10,6 @@ from fake_requests import MockResponse - def mock_cloudfoundry_client_class(): if not getattr(CloudFoundryClient, 'CLASS_MOCKED', False): mocked_attributes = ['get', 'post', 'patch', 'put', 'delete'] @@ -44,7 +43,7 @@ def build_client(self): self.client = CloudFoundryClient(self.TARGET_ENDPOINT) @staticmethod - def _mock_info_calls(requests): + def _mock_info_calls(requests, with_doppler: bool = True, with_log_streams: bool = True): requests.get.side_effect = [ MockResponse( '%s/v2/info' % AbstractTestCase.TARGET_ENDPOINT, @@ -60,8 +59,8 @@ def _mock_info_calls(requests): meta=dict(version=AbstractTestCase.API_V2_VERSION)), 'cloud_controller_v3': dict(href='%s/v3' % AbstractTestCase.TARGET_ENDPOINT, meta=dict(version=AbstractTestCase.API_V3_VERSION)), - 'logging': dict(href=AbstractTestCase.DOPPLER_ENDPOINT), - 'log_stream': dict(href=AbstractTestCase.LOG_STREAM_ENDPOINT), + 'logging': dict(href=AbstractTestCase.DOPPLER_ENDPOINT) if with_doppler else None, + 'log_stream': dict(href=AbstractTestCase.LOG_STREAM_ENDPOINT) if with_log_streams else None, 'app_ssh': dict(href='ssh.nd-cfapi.itn.ftgroup:80'), 'uaa': dict(href='https://uaa.nd-cfapi.itn.ftgroup'), 'network_policy_v0': dict( diff --git a/test/test_client.py b/test/test_client.py index b602815..f1f85b2 100644 --- a/test/test_client.py +++ b/test/test_client.py @@ -14,6 +14,26 @@ class TestCloudfoundryClient(unittest.TestCase, AbstractTestCase, ): def setUpClass(cls): cls.mock_client_class() + def test_build_client_when_no_log_stream(self): + requests = FakeRequests() + session = MockSession() + with patch('oauth2_client.credentials_manager.requests', new=requests), \ + patch('cloudfoundry_client.client.requests', new=requests): + requests.Session.return_value = session + self._mock_info_calls(requests, with_log_streams=False) + client = CloudFoundryClient(self.TARGET_ENDPOINT, token_format='opaque') + self.assertRaises(NotImplementedError, lambda: client.rlpgateway) + + def test_build_client_when_no_doppler(self): + requests = FakeRequests() + session = MockSession() + with patch('oauth2_client.credentials_manager.requests', new=requests), \ + patch('cloudfoundry_client.client.requests', new=requests): + requests.Session.return_value = session + self._mock_info_calls(requests, with_doppler=False) + client = CloudFoundryClient(self.TARGET_ENDPOINT, token_format='opaque') + self.assertRaises(NotImplementedError, lambda: client.doppler) + def test_grant_password_request_with_token_format_opaque(self): requests = FakeRequests() session = MockSession() From a9635e9d9ae4aa10d1037e5040d9dbf7aad4cef3 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Thu, 2 Apr 2020 18:36:29 +0200 Subject: [PATCH 037/264] Prepare 1.12.2 release --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index eff74ab..215a365 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.12.1" +__version__ = "1.12.2" From 1c24295a9d09fb22a12b6bb2063e4c221ddfb9e9 Mon Sep 17 00:00:00 2001 From: Alina Golovchenko Date: Fri, 3 Apr 2020 15:05:34 +0200 Subject: [PATCH 038/264] Iterate RLP content --- main/cloudfoundry_client/rlpgateway/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/rlpgateway/client.py b/main/cloudfoundry_client/rlpgateway/client.py index 3d6c413..5520482 100644 --- a/main/cloudfoundry_client/rlpgateway/client.py +++ b/main/cloudfoundry_client/rlpgateway/client.py @@ -41,5 +41,5 @@ async def stream_logs(self, app_guid, **kwargs): if response.status == 204: yield {} else: - async for data in response.content.iter_any(): + async for data in response.content: yield data From c1edca484f25517ad8353f1a1654df5c7757bc4d Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sun, 5 Apr 2020 19:04:04 +0200 Subject: [PATCH 039/264] Fix HTTPStatus handle in errors --- main/cloudfoundry_client/__init__.py | 2 +- main/cloudfoundry_client/client.py | 4 ++-- main/cloudfoundry_client/errors.py | 6 +++--- requirements.txt | 2 +- test/abstract_test_case.py | 8 ++++---- test/fake_requests.py | 4 ++-- test/test_client.py | 6 +++--- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 215a365..4c398d2 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.12.2" +__version__ = "1.12.3" diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 92baa9d..5feade3 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -247,6 +247,6 @@ def _check_response(response: Response) -> Response: else: try: body = response.json() - except Exception as _: + except ValueError as _: body = response.text - raise InvalidStatusCode(response.status_code, body) + raise InvalidStatusCode(HTTPStatus(response.status_code), body) diff --git a/main/cloudfoundry_client/errors.py b/main/cloudfoundry_client/errors.py index 428d504..d4b3d16 100644 --- a/main/cloudfoundry_client/errors.py +++ b/main/cloudfoundry_client/errors.py @@ -13,11 +13,11 @@ def __init__(self, status_code: HTTPStatus, body): def __str__(self): if self.body is None: - return '%d' % self.status_code.value + return self.status_code.name elif type(self.body) == str: - return '%d : %s' % (self.status_code.value, self.body) + return '%s : %s' % (self.status_code.name, self.body) else: - return '%d : %s' % (self.status_code.value, json.dumps(self.body)) + return '%s : %s' % (self.status_code.name, json.dumps(self.body)) class InvalidEntity(Exception): diff --git a/requirements.txt b/requirements.txt index 79bd69a..71aaf4b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ aiohttp==3.6.2 protobuf==3.6.1 -oauth2-client==1.2.0 +oauth2-client==1.2.1 websocket-client==0.53.0 PyYAML==5.1 diff --git a/test/abstract_test_case.py b/test/abstract_test_case.py index 464b8dc..93cbcda 100644 --- a/test/abstract_test_case.py +++ b/test/abstract_test_case.py @@ -47,12 +47,12 @@ def _mock_info_calls(requests, with_doppler: bool = True, with_log_streams: bool requests.get.side_effect = [ MockResponse( '%s/v2/info' % AbstractTestCase.TARGET_ENDPOINT, - status_code=HTTPStatus.OK, + status_code=HTTPStatus.OK.value, text=json.dumps(dict(authorization_endpoint=AbstractTestCase.AUTHORIZATION_ENDPOINT, token_endpoint=AbstractTestCase.TOKEN_ENDPOINT))), MockResponse( '%s/' % AbstractTestCase.TARGET_ENDPOINT, - status_code=HTTPStatus.OK, + status_code=HTTPStatus.OK.value, text=json.dumps(dict(links={ 'self': dict(href=AbstractTestCase.TARGET_ENDPOINT), 'cloud_controller_v2': dict(href='%s/v2' % AbstractTestCase.TARGET_ENDPOINT, @@ -83,10 +83,10 @@ def mock_response(uri: str, status_code: HTTPStatus, headers: Optional[dict], *p with(open(AbstractTestCase.get_fixtures_path(*path_parts), 'rb' if binary_file else 'r')) as f: return MockResponse(url='%s%s' % (AbstractTestCase.TARGET_ENDPOINT, uri), - status_code=status_code, + status_code=status_code.value, text=f.read(), headers=headers) else: return MockResponse('%s%s' % (AbstractTestCase.TARGET_ENDPOINT, uri), - status_code, + status_code.value, '') diff --git a/test/fake_requests.py b/test/fake_requests.py index 0f44feb..c0469b4 100644 --- a/test/fake_requests.py +++ b/test/fake_requests.py @@ -17,8 +17,8 @@ def __init__(self): class MockResponse(object): - def __init__(self, url: str, status_code: HTTPStatus, text: str, headers: dict = None): - self.status_code = status_code.value + def __init__(self, url: str, status_code: int, text: str, headers: dict = None): + self.status_code = status_code self.url = url self.text = text self.headers = dict() diff --git a/test/test_client.py b/test/test_client.py index f1f85b2..ee69a40 100644 --- a/test/test_client.py +++ b/test/test_client.py @@ -42,7 +42,7 @@ def test_grant_password_request_with_token_format_opaque(self): requests.Session.return_value = session self._mock_info_calls(requests) requests.post.return_value = MockResponse('%s/oauth/token' % self.TOKEN_ENDPOINT, - status_code=HTTPStatus.OK, + status_code=HTTPStatus.OK.value, text=json.dumps(dict(access_token='access-token', refresh_token='refresh-token'))) client = CloudFoundryClient(self.TARGET_ENDPOINT, token_format='opaque') @@ -66,7 +66,7 @@ def test_refresh_request_with_token_format_opaque(self): requests.Session.return_value = session self._mock_info_calls(requests) requests.post.return_value = MockResponse('%s/oauth/token' % self.TOKEN_ENDPOINT, - status_code=HTTPStatus.OK, + status_code=HTTPStatus.OK.value, text=json.dumps(dict(access_token='access-token', refresh_token='refresh-token'))) client = CloudFoundryClient(self.TARGET_ENDPOINT, token_format='opaque') @@ -89,7 +89,7 @@ def test_grant_password_request_with_login_hint(self): requests.Session.return_value = session self._mock_info_calls(requests) requests.post.return_value = MockResponse('%s/oauth/token' % self.TOKEN_ENDPOINT, - status_code=HTTPStatus.OK, + status_code=HTTPStatus.OK.value, text=json.dumps(dict(access_token='access-token', refresh_token='refresh-token'))) client = CloudFoundryClient(self.TARGET_ENDPOINT, login_hint=quote(json.dumps(dict(origin='uaa'), From 324218586b4930eb71b4ecdf79a08d9c6875f233 Mon Sep 17 00:00:00 2001 From: Alina Golovchenko Date: Fri, 3 Apr 2020 15:05:34 +0200 Subject: [PATCH 040/264] Iterate RLP content --- main/cloudfoundry_client/rlpgateway/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/rlpgateway/client.py b/main/cloudfoundry_client/rlpgateway/client.py index 3d6c413..5520482 100644 --- a/main/cloudfoundry_client/rlpgateway/client.py +++ b/main/cloudfoundry_client/rlpgateway/client.py @@ -41,5 +41,5 @@ async def stream_logs(self, app_guid, **kwargs): if response.status == 204: yield {} else: - async for data in response.content.iter_any(): + async for data in response.content: yield data From 97f334f9853ae935f3b5a6f06948b9661b7562a0 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sun, 5 Apr 2020 19:11:28 +0200 Subject: [PATCH 041/264] Prepare 1.12.4 release --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 4c398d2..c782302 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.12.3" +__version__ = "1.12.4" From fb4e592a9682e705c6fbb394c7c039a86262e639 Mon Sep 17 00:00:00 2001 From: Alina Golovchenko Date: Tue, 14 Apr 2020 09:14:30 +0200 Subject: [PATCH 042/264] Iterate chunks of data in RLP stream --- main/cloudfoundry_client/rlpgateway/client.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/main/cloudfoundry_client/rlpgateway/client.py b/main/cloudfoundry_client/rlpgateway/client.py index 5520482..0766b8e 100644 --- a/main/cloudfoundry_client/rlpgateway/client.py +++ b/main/cloudfoundry_client/rlpgateway/client.py @@ -41,5 +41,10 @@ async def stream_logs(self, app_guid, **kwargs): if response.status == 204: yield {} else: - async for data in response.content: - yield data + buffer = b"" + async for data in response.content.iter_chunked(1024): + buffer += data + if b"\n\n" in buffer and buffer.startswith(b"data:"): + log_message = buffer.split(b"\n\n")[0] + buffer = buffer.replace(log_message + b"\n\n", b"") + yield log_message From 4e87277ad7112484b0b635058f3dd8f3b67b2093 Mon Sep 17 00:00:00 2001 From: Philipp Thun Date: Thu, 16 Apr 2020 14:20:59 +0200 Subject: [PATCH 043/264] Properly detect CF-InvalidAuthToken errors returned by V3 API endpoints See "error object" in V3 API documentation: https://v3-apidocs.cloudfoundry.org/version/3.82.0/index.html#errors --- main/cloudfoundry_client/client.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 5feade3..be9f7b7 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -187,7 +187,14 @@ def _is_token_expired(response: Response) -> bool: if response.status_code == HTTPStatus.UNAUTHORIZED.value: try: json_data = response.json() - result = json_data.get('code', 0) == 1000 and json_data.get('error_code', '') == 'CF-InvalidAuthToken' + if json_data.get('errors'): # V3 error response + result = False + for error in json_data.get('errors'): + if error.get('code', 0) == 1000 and error.get('title', '') == 'CF-InvalidAuthToken': + result = True + break + else: # V2 error response + result = json_data.get('code', 0) == 1000 and json_data.get('error_code', '') == 'CF-InvalidAuthToken' _logger.info('_is_token_expired - %s' % str(result)) return result except BaseException as _: From 1ce942a7e0fe6fcf641f55ee648683a1879b2e11 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Fri, 17 Apr 2020 22:34:00 +0200 Subject: [PATCH 044/264] Fix authentication endpoint Authentication shall be deduced from 'authorization_endpoint' info attribute Close #87 --- main/cloudfoundry_client/client.py | 11 ++++++----- test/test_client.py | 6 +++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index be9f7b7..06033de 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -39,13 +39,11 @@ class Info: def __init__(self, api_v2_version: str, authorization_endpoint: str, - token_endpoint: str, api_endpoint: str, doppler_endpoint: Optional[str], log_stream_endpoint: Optional[str]): self.api_v2_version = api_v2_version self.authorization_endpoint = authorization_endpoint - self.token_endpoint = token_endpoint self.api_endpoint = api_endpoint self.doppler_endpoint = doppler_endpoint self.log_stream_endpoint = log_stream_endpoint @@ -124,8 +122,8 @@ def __init__(self, target_endpoint: str, client_id: str = 'cf', client_secret: s info = self._get_info(target_endpoint_trimmed, proxy, verify=verify) if not info.api_v2_version.startswith('2.'): raise AssertionError('Only version 2 is supported for now. Found %s' % info.api_v2_version) - service_information = ServiceInformation('%s/oauth/authorize' % info.authorization_endpoint, - '%s/oauth/token' % info.token_endpoint, + service_information = ServiceInformation(None, + '%s/oauth/token' % info.authorization_endpoint, client_id, client_secret, [], verify) super(CloudFoundryClient, self).__init__(service_information, proxies=proxy) self.v2 = V2(target_endpoint_trimmed, self) @@ -177,7 +175,6 @@ def _get_info(target_endpoint: str, proxy: Optional[dict] = None, verify: bool = log_stream = root_links.get('log_stream') return Info(root_links['cloud_controller_v2']['meta']['version'], info['authorization_endpoint'], - info['token_endpoint'], target_endpoint, logging.get('href') if logging is not None else None, log_stream.get('href') if log_stream is not None else None) @@ -227,6 +224,10 @@ def _grant_refresh_token_request(self, refresh_token: str) -> dict: request['token_format'] = self.token_format return request + def _grant_client_credentials_request(self) -> dict: + return dict(grant_type="client_credentials", scope=' '.join(self.service_information.scopes), + client_id=self.service_information.client_id, client_secret=self.service_information.client_secret) + def get(self, url: str, params: Optional[dict] = None, **kwargs) -> Response: response = super(CloudFoundryClient, self).get(url, params, **kwargs) return CloudFoundryClient._check_response(response) diff --git a/test/test_client.py b/test/test_client.py index ee69a40..2199c2f 100644 --- a/test/test_client.py +++ b/test/test_client.py @@ -41,7 +41,7 @@ def test_grant_password_request_with_token_format_opaque(self): patch('cloudfoundry_client.client.requests', new=requests): requests.Session.return_value = session self._mock_info_calls(requests) - requests.post.return_value = MockResponse('%s/oauth/token' % self.TOKEN_ENDPOINT, + requests.post.return_value = MockResponse('%s/oauth/token' % self.AUTHORIZATION_ENDPOINT, status_code=HTTPStatus.OK.value, text=json.dumps(dict(access_token='access-token', refresh_token='refresh-token'))) @@ -65,7 +65,7 @@ def test_refresh_request_with_token_format_opaque(self): patch('cloudfoundry_client.client.requests', new=requests): requests.Session.return_value = session self._mock_info_calls(requests) - requests.post.return_value = MockResponse('%s/oauth/token' % self.TOKEN_ENDPOINT, + requests.post.return_value = MockResponse('%s/oauth/token' % self.AUTHORIZATION_ENDPOINT, status_code=HTTPStatus.OK.value, text=json.dumps(dict(access_token='access-token', refresh_token='refresh-token'))) @@ -88,7 +88,7 @@ def test_grant_password_request_with_login_hint(self): patch('cloudfoundry_client.client.requests', new=requests): requests.Session.return_value = session self._mock_info_calls(requests) - requests.post.return_value = MockResponse('%s/oauth/token' % self.TOKEN_ENDPOINT, + requests.post.return_value = MockResponse('%s/oauth/token' % self.AUTHORIZATION_ENDPOINT, status_code=HTTPStatus.OK.value, text=json.dumps(dict(access_token='access-token', refresh_token='refresh-token'))) From 7cb57a005c7c1ebfdadf7cd2bfc8e5fcb94d198d Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Tue, 21 Apr 2020 08:48:15 +0200 Subject: [PATCH 045/264] Added unit tests for _is_token_expired' --- main/cloudfoundry_client/client.py | 27 +++++++++++++++------------ test/test_client.py | 15 +++++++++++++++ 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index be9f7b7..d5edcd4 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -167,9 +167,9 @@ def _get_info(target_endpoint: str, proxy: Optional[dict] = None, verify: bool = verify=verify)) info = info_response.json() root_response = CloudFoundryClient._check_response(requests.get('%s/' % target_endpoint, - proxies=proxy if proxy is not None else dict( - http='', https=''), - verify=verify)) + proxies=proxy if proxy is not None else dict( + http='', https=''), + verify=verify)) root_info = root_response.json() root_links = root_info['links'] @@ -187,17 +187,20 @@ def _is_token_expired(response: Response) -> bool: if response.status_code == HTTPStatus.UNAUTHORIZED.value: try: json_data = response.json() - if json_data.get('errors'): # V3 error response - result = False + invalid_token_error = 'CF-InvalidAuthToken' + if json_data.get('errors', None) is not None: # V3 error response for error in json_data.get('errors'): - if error.get('code', 0) == 1000 and error.get('title', '') == 'CF-InvalidAuthToken': - result = True - break + if error.get('code', 0) == 1000 and error.get('title', '') == invalid_token_error: + _logger.info('_is_token_v3_expired - true') + return True + _logger.info('_is_token_v3_expired - false') + return False else: # V2 error response - result = json_data.get('code', 0) == 1000 and json_data.get('error_code', '') == 'CF-InvalidAuthToken' - _logger.info('_is_token_expired - %s' % str(result)) - return result - except BaseException as _: + token_expired = json_data.get('code', 0) == 1000 \ + and json_data.get('error_code', '') == invalid_token_error + _logger.info('_is_token__v2_expired - %s' % str(token_expired)) + return token_expired + except Exception as _: return False else: return False diff --git a/test/test_client.py b/test/test_client.py index ee69a40..037d988 100644 --- a/test/test_client.py +++ b/test/test_client.py @@ -118,3 +118,18 @@ def test_get_info(self): self.assertEqual(info.api_v2_version, self.API_V2_VERSION) self.assertEqual(info.doppler_endpoint, self.DOPPLER_ENDPOINT) self.assertEqual(info.log_stream_endpoint, self.LOG_STREAM_ENDPOINT) + + def test_invalid_token_v3(self): + response = MockResponse('http://some-cf-url', 401, text=json.dumps(dict( + errors=[dict(code=666, title='Some-Error', detail='Error detail'), + dict(code=1000, title='CF-InvalidAuthToken', detail='Invalid token')] + ))) + result = CloudFoundryClient._is_token_expired(response) + self.assertTrue(result) + + def test_invalid_token_v2(self): + response = MockResponse('http://some-cf-url', 401, + text=json.dumps(dict(code=1000, error_code='CF-InvalidAuthToken') + )) + result = CloudFoundryClient._is_token_expired(response) + self.assertTrue(result) From ba4ee3e40672a59b77e39c92914ef7459f6d45ec Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Wed, 22 Apr 2020 18:51:05 +0200 Subject: [PATCH 046/264] Prepare 1.12.5 release --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index c782302..72d479f 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.12.4" +__version__ = "1.12.5" From f01a7bcf3c93285ee0ef4f22b5ebaa295b88837c Mon Sep 17 00:00:00 2001 From: Karthikeyan Singaravelan Date: Fri, 22 May 2020 04:10:59 +0000 Subject: [PATCH 047/264] Fix deprecation warnings due to invalid escape sequences. --- main/cloudfoundry_client/operations/push/push.py | 4 ++-- .../operations/push/validation/manifest.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/main/cloudfoundry_client/operations/push/push.py b/main/cloudfoundry_client/operations/push/push.py index cbe0d27..77f08b2 100644 --- a/main/cloudfoundry_client/operations/push/push.py +++ b/main/cloudfoundry_client/operations/push/push.py @@ -19,7 +19,7 @@ class PushOperation(object): UPLOAD_TIMEOUT = 15 * 60 - SPLIT_ROUTE_PATTERN = re.compile('(?P[a-z]+://)?(?P[^:/]+)(?P:\d+)?(?P/.*)?') + SPLIT_ROUTE_PATTERN = re.compile(r'(?P[a-z]+://)?(?P[^:/]+)(?P:\d+)?(?P/.*)?') def __init__(self, client: CloudFoundryClient): self.client = client @@ -340,7 +340,7 @@ def _restart_application(app: Entity): @staticmethod def _to_host(host: str) -> str: def no_space(h: str) -> str: - return re.sub('[\s_]+', "-", h) + return re.sub(r'[\s_]+', "-", h) def only_alphabetical_and_hyphen(h: str) -> str: return re.sub("[^a-z0-9-]", "", h) diff --git a/main/cloudfoundry_client/operations/push/validation/manifest.py b/main/cloudfoundry_client/operations/push/validation/manifest.py index ef00724..7ad44ff 100644 --- a/main/cloudfoundry_client/operations/push/validation/manifest.py +++ b/main/cloudfoundry_client/operations/push/validation/manifest.py @@ -6,7 +6,7 @@ class ManifestReader(object): - MEMORY_PATTERN = re.compile("^(\d+)([KMGT])B?$") + MEMORY_PATTERN = re.compile(r"^(\d+)([KMGT])B?$") POSITIVE_FIELDS = ['instances', 'timeout'] From 2db8dab353925a26bcceadfce9c536e8620943bd Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sat, 23 May 2020 14:16:05 +0200 Subject: [PATCH 048/264] Fix Push._split_route signature --- main/cloudfoundry_client/operations/push/push.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/operations/push/push.py b/main/cloudfoundry_client/operations/push/push.py index 77f08b2..a04d72b 100644 --- a/main/cloudfoundry_client/operations/push/push.py +++ b/main/cloudfoundry_client/operations/push/push.py @@ -197,7 +197,7 @@ def _resolve_new_tcp_route(self, space: Entity, domain: Entity, port: int) -> En return existing_route @staticmethod - def _split_route(requested_route: str) -> Tuple[str, int, str]: + def _split_route(requested_route: Dict[str, str]) -> Tuple[str, int, str]: route_splitted = PushOperation.SPLIT_ROUTE_PATTERN.match(requested_route['route']) if route_splitted is None: raise AssertionError('Invalid route: %s' % requested_route['route']) From 259feb4cc4b93a62cb37a7fcded39318b48e6d18 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Wed, 3 Jun 2020 07:32:41 +0200 Subject: [PATCH 049/264] Add get env in v3 API Closes #94 --- main/cloudfoundry_client/v3/apps.py | 4 ++ .../v3/apps/GET_{id}_env_response.json | 50 +++++++++++++++++++ test/v3/test_apps.py | 8 +++ 3 files changed, 62 insertions(+) create mode 100644 test/fixtures/v3/apps/GET_{id}_env_response.json diff --git a/main/cloudfoundry_client/v3/apps.py b/main/cloudfoundry_client/v3/apps.py index 718cfed..014d22d 100644 --- a/main/cloudfoundry_client/v3/apps.py +++ b/main/cloudfoundry_client/v3/apps.py @@ -1,3 +1,4 @@ +from cloudfoundry_client.json_object import JsonObject from cloudfoundry_client.v3.entities import EntityManager @@ -7,3 +8,6 @@ def __init__(self, target_endpoint: str, client: 'CloudfoundryClient'): def remove(self, application_guid: str): super(AppManager, self)._remove(application_guid) + + def get_env(self, application_guid: str) -> JsonObject: + return super(AppManager, self)._get('%s%s/%s/env' % (self.target_endpoint, self.entity_uri, application_guid)) diff --git a/test/fixtures/v3/apps/GET_{id}_env_response.json b/test/fixtures/v3/apps/GET_{id}_env_response.json new file mode 100644 index 0000000..7976bfe --- /dev/null +++ b/test/fixtures/v3/apps/GET_{id}_env_response.json @@ -0,0 +1,50 @@ +{ + "staging_env_json": { + "GEM_CACHE": "http://gem-cache.example.org" + }, + "running_env_json": { + "HTTP_PROXY": "http://proxy.example.org" + }, + "environment_variables": { + "RAILS_ENV": "production" + }, + "system_env_json": { + "VCAP_SERVICES": { + "mysql": [ + { + "name": "db-for-my-app", + "label": "mysql", + "tags": [ + "relational", + "sql" + ], + "plan": "xlarge", + "credentials": { + "username": "user", + "password": "top-secret" + }, + "syslog_drain_url": "https://syslog.example.org/drain", + "provider": null + } + ] + } + }, + "application_env_json": { + "VCAP_APPLICATION": { + "limits": { + "fds": 16384 + }, + "application_name": "my_app", + "application_uris": [ + "my_app.example.org" + ], + "name": "my_app", + "space_name": "my_space", + "space_id": "2f35885d-0c9d-4423-83ad-fd05066f8576", + "uris": [ + "my_app.example.org" + ], + "users": null + } + } +} \ No newline at end of file diff --git a/test/v3/test_apps.py b/test/v3/test_apps.py index 28a23ca..59e2639 100644 --- a/test/v3/test_apps.py +++ b/test/v3/test_apps.py @@ -2,6 +2,7 @@ from http import HTTPStatus from abstract_test_case import AbstractTestCase +from cloudfoundry_client.json_object import JsonObject from cloudfoundry_client.v3.entities import Entity @@ -65,3 +66,10 @@ def test_remove(self): '/v3/apps/app_id', HTTPStatus.NO_CONTENT, None) self.client.v3.apps.remove('app_id') self.client.delete.assert_called_with(self.client.delete.return_value.url) + + def test_get_env(self): + self.client.get.return_value = self.mock_response('/v3/apps/app_id/env', HTTPStatus.OK, None, + 'v3', 'apps', 'GET_{id}_env_response.json') + env = self.client.v3.apps.get_env('app_id') + self.assertIsInstance(env, JsonObject) + self.assertEquals(env['application_env_json']['VCAP_APPLICATION']['limits']['fds'], 16384) From 200e5d883896843b15ea51cdc270e8ec88e320f4 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Wed, 3 Jun 2020 08:55:04 +0200 Subject: [PATCH 050/264] Prepare 1.13.0 release --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 72d479f..95feb82 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.12.5" +__version__ = "1.13.0" From c3988603afa528d7835ae45e199d4b36ae540afb Mon Sep 17 00:00:00 2001 From: Abhishek Date: Thu, 4 Jun 2020 01:35:48 -0500 Subject: [PATCH 051/264] Adding new method to retrive app routes --- main/cloudfoundry_client/v3/apps.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/main/cloudfoundry_client/v3/apps.py b/main/cloudfoundry_client/v3/apps.py index 014d22d..82daa5e 100644 --- a/main/cloudfoundry_client/v3/apps.py +++ b/main/cloudfoundry_client/v3/apps.py @@ -11,3 +11,6 @@ def remove(self, application_guid: str): def get_env(self, application_guid: str) -> JsonObject: return super(AppManager, self)._get('%s%s/%s/env' % (self.target_endpoint, self.entity_uri, application_guid)) + + def get_routes(self, application_guid: str) -> JsonObject: + return super(AppManager, self)._get('%s%s/%s/routes' % (self.target_endpoint, self.entity_uri, application_guid)) From 65d1a122571f0c3ef5d4a8e8dc4e555e3c36436d Mon Sep 17 00:00:00 2001 From: Abhishek Date: Thu, 4 Jun 2020 01:39:49 -0500 Subject: [PATCH 052/264] Adding test case --- .../v3/apps/GET_{id}_routes_response.json | 80 +++++++++++++++++++ test/v3/test_apps.py | 23 ++++-- 2 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 test/fixtures/v3/apps/GET_{id}_routes_response.json diff --git a/test/fixtures/v3/apps/GET_{id}_routes_response.json b/test/fixtures/v3/apps/GET_{id}_routes_response.json new file mode 100644 index 0000000..5a54cf0 --- /dev/null +++ b/test/fixtures/v3/apps/GET_{id}_routes_response.json @@ -0,0 +1,80 @@ +{ + "pagination": { + "total_results": 3, + "total_pages": 2, + "first": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/routes?page=1&per_page=2" + }, + "last": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/routes?page=2&per_page=2" + }, + "next": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/routes?page=2&per_page=2" + }, + "previous": null + }, + "resources": [ + { + "guid": "cbad697f-cac1-48f4-9017-ac08f39dfb31", + "created_at": "2019-05-10T17:17:48Z", + "updated_at": "2019-05-10T17:17:48Z", + "host": "a-hostname", + "path": "/some_path", + "url": "a-hostname.a-domain.com/some_path", + "destinations": [ + { + "guid": "385bf117-17f5-4689-8c5c-08c6cc821fed", + "app": { + "guid": "0a6636b5-7fc4-44d8-8752-0db3e40b35a5", + "process": { + "type": "web" + } + }, + "weight": null, + "port": 8080 + }, + { + "guid": "27e96a3b-5bcf-49ed-8048-351e0be23e6f", + "app": { + "guid": "f61e59fa-2121-4217-8c7b-15bfd75baf25", + "process": { + "type": "web" + } + }, + "weight": null, + "port": 8080 + } + ], + "metadata": { + "labels": {}, + "annotations": {} + }, + "relationships": { + "space": { + "data": { + "guid": "885a8cb3-c07b-4856-b448-eeb10bf36236" + } + }, + "domain": { + "data": { + "guid": "0b5f3633-194c-42d2-9408-972366617e0e" + } + } + }, + "links": { + "self": { + "href": "http://somewhere.org/v3/routes/cbad697f-cac1-48f4-9017-ac08f39dfb31" + }, + "space": { + "href": "http://somewhere.org/v3/spaces/885a8cb3-c07b-4856-b448-eeb10bf36236" + }, + "domain": { + "href": "http://somewhere.org/v3/domains/0b5f3633-194c-42d2-9408-972366617e0e" + }, + "destinations": { + "href": "http://somewhere.org/v3/routes/cbad697f-cac1-48f4-9017-ac08f39dfb31/destinations" + } + } + } + ] +} diff --git a/test/v3/test_apps.py b/test/v3/test_apps.py index 59e2639..5028b51 100644 --- a/test/v3/test_apps.py +++ b/test/v3/test_apps.py @@ -19,7 +19,8 @@ def test_list(self): HTTPStatus.OK, None, 'v3', 'apps', 'GET_response.json') - all_applications = [application for application in self.client.v3.apps.list()] + all_applications = [ + application for application in self.client.v3.apps.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(2, len(all_applications)) self.assertEqual(all_applications[0]['name'], "my_app") @@ -36,7 +37,8 @@ def test_get(self): self.assertIsInstance(application, Entity) def test_get_then_space(self): - get_app = self.mock_response('/v3/apps/app_id', HTTPStatus.OK, None, 'v3', 'apps', 'GET_{id}_response.json') + get_app = self.mock_response( + '/v3/apps/app_id', HTTPStatus.OK, None, 'v3', 'apps', 'GET_{id}_response.json') get_space = self.mock_response('/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576', HTTPStatus.OK, None, 'v3', 'spaces', 'GET_{id}_response.json') self.client.get.side_effect = [ @@ -57,7 +59,8 @@ def test_get_then_start(self): app = self.client.v3.apps.get('app_id').start() self.client.get.assert_called_with(self.client.get.return_value.url) - self.client.post.assert_called_with(self.client.post.return_value.url, files=None, json=None) + self.client.post.assert_called_with( + self.client.post.return_value.url, files=None, json=None) self.assertEqual("my_app", app['name']) self.assertIsInstance(app, Entity) @@ -65,11 +68,21 @@ def test_remove(self): self.client.delete.return_value = self.mock_response( '/v3/apps/app_id', HTTPStatus.NO_CONTENT, None) self.client.v3.apps.remove('app_id') - self.client.delete.assert_called_with(self.client.delete.return_value.url) + self.client.delete.assert_called_with( + self.client.delete.return_value.url) def test_get_env(self): self.client.get.return_value = self.mock_response('/v3/apps/app_id/env', HTTPStatus.OK, None, 'v3', 'apps', 'GET_{id}_env_response.json') env = self.client.v3.apps.get_env('app_id') self.assertIsInstance(env, JsonObject) - self.assertEquals(env['application_env_json']['VCAP_APPLICATION']['limits']['fds'], 16384) + self.assertEquals(env['application_env_json'] + ['VCAP_APPLICATION']['limits']['fds'], 16384) + + def test_get_routes(self): + self.client.get.return_value = self.mock_response('/v3/apps/app_id/routes', HTTPStatus.OK, None, + 'v3', 'apps', 'GET_{id}_routes_response.json') + routes = self.client.v3.apps.get_routes('app_id') + self.assertIsInstance(routes, JsonObject) + self.assertEquals(routes['resources'][0]['guid'], + "385bf117-17f5-4689-8c5c-08c6cc821fed") From 3c789619ddc3c6911aba3a9124a9edd3dd32dd3c Mon Sep 17 00:00:00 2001 From: Abhishek Date: Thu, 4 Jun 2020 10:30:56 -0500 Subject: [PATCH 053/264] bug fix --- test/v3/test_apps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/v3/test_apps.py b/test/v3/test_apps.py index 5028b51..b18b736 100644 --- a/test/v3/test_apps.py +++ b/test/v3/test_apps.py @@ -84,5 +84,5 @@ def test_get_routes(self): 'v3', 'apps', 'GET_{id}_routes_response.json') routes = self.client.v3.apps.get_routes('app_id') self.assertIsInstance(routes, JsonObject) - self.assertEquals(routes['resources'][0]['guid'], + self.assertEquals(routes['resources'][0]['destinations'][0]['guid'], "385bf117-17f5-4689-8c5c-08c6cc821fed") From bcd85713ec000514610dcb30486b26f9d18c4da8 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sun, 5 Jul 2020 18:54:50 +0200 Subject: [PATCH 054/264] Prepare 1.14.0 release --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 95feb82..f1f24de 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.13.0" +__version__ = "1.14.0" From 40857e73aa0415bb894f62bbb30729b3cf334a18 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sun, 5 Jul 2020 19:00:38 +0200 Subject: [PATCH 055/264] Upgrade PyYAML dependency to 5.3.1 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 71aaf4b..0be3931 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ aiohttp==3.6.2 protobuf==3.6.1 oauth2-client==1.2.1 websocket-client==0.53.0 -PyYAML==5.1 +PyYAML==5.3.1 From 50dae8672f6d6e3cab76d368644090fd88f6642e Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sun, 5 Jul 2020 19:03:53 +0200 Subject: [PATCH 056/264] Prepare 1.14.1 release --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index f1f24de..7caa26c 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.14.0" +__version__ = "1.14.1" From 1feb6e1630cfd76734c7b43c9131f24a6c1ff296 Mon Sep 17 00:00:00 2001 From: Alina Golovchenko Date: Fri, 28 Aug 2020 16:55:41 +0200 Subject: [PATCH 057/264] Keep RLP connection alive --- main/cloudfoundry_client/rlpgateway/client.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/main/cloudfoundry_client/rlpgateway/client.py b/main/cloudfoundry_client/rlpgateway/client.py index 0766b8e..72dc1c2 100644 --- a/main/cloudfoundry_client/rlpgateway/client.py +++ b/main/cloudfoundry_client/rlpgateway/client.py @@ -30,8 +30,12 @@ def __init__(self, rlp_gateway_endpoint, proxy, verify_ssl, credentials_manager) async def stream_logs(self, app_guid, **kwargs): url = f"{self.rlp_gateway_endpoint}/v2/read" - headers = dict(Authorization=self.credentials_manager._access_token) - params = dict(log="", source_id=app_guid) + headers = { + "Authorization": self.credentials_manager._access_token, + "Accept": "text/event-stream", + "Cache-Control": "no-cache", + } + params = {"log": "", "source_id": app_guid} if "headers" in kwargs: headers.update(kwargs["headers"]) if "params" in kwargs: @@ -48,3 +52,11 @@ async def stream_logs(self, app_guid, **kwargs): log_message = buffer.split(b"\n\n")[0] buffer = buffer.replace(log_message + b"\n\n", b"") yield log_message + elif buffer.startswith( + b"event: heartbeat" + ) or buffer.startswith(b"event: closing"): + # Consume heartbeats to keep the connection alive + buffer = b"" + yield data + else: + yield data From 898a982a27c1c6524fe7aa0de5965b2abbdeddd1 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sat, 12 Dec 2020 17:52:29 +0100 Subject: [PATCH 058/264] Upgrade websocket version See #109 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0be3931..132a176 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ aiohttp==3.6.2 protobuf==3.6.1 oauth2-client==1.2.1 -websocket-client==0.53.0 +websocket-client==0.54.0 PyYAML==5.3.1 From 1cb36bbc9b64cc3b9de919b55bada89219de475b Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Wed, 16 Dec 2020 14:44:55 +0100 Subject: [PATCH 059/264] Prepare '1.15.0' version --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 7caa26c..8128f81 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.14.1" +__version__ = "1.15.0" From fbe28a28c4d8700d682cace83aedc5ff5ddfdde9 Mon Sep 17 00:00:00 2001 From: Sven Krieger Date: Thu, 18 Feb 2021 17:53:04 +0100 Subject: [PATCH 060/264] Add v3 JobManager and job link to entities Co-authored-by: Sven Krieger Co-authored-by: Florian Braun --- main/cloudfoundry_client/client.py | 2 + main/cloudfoundry_client/v3/entities.py | 6 + main/cloudfoundry_client/v3/jobs.py | 33 +++++ requirements.txt | 1 + .../v3/jobs/GET_{id}_complete_response.json | 14 +++ .../v3/jobs/GET_{id}_failed_response.json | 27 ++++ .../v3/jobs/GET_{id}_processing_response.json | 14 +++ test/v3/test_buildpacks.py | 20 ++- test/v3/test_jobs.py | 115 ++++++++++++++++++ 9 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 main/cloudfoundry_client/v3/jobs.py create mode 100644 test/fixtures/v3/jobs/GET_{id}_complete_response.json create mode 100644 test/fixtures/v3/jobs/GET_{id}_failed_response.json create mode 100644 test/fixtures/v3/jobs/GET_{id}_processing_response.json create mode 100644 test/v3/test_jobs.py diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 730146c..af347ff 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -32,6 +32,7 @@ from cloudfoundry_client.v3.organizations import OrganizationManager from cloudfoundry_client.v3.spaces import SpaceManager from cloudfoundry_client.v3.tasks import TaskManager +from cloudfoundry_client.v3.jobs import JobManager as JobManagerV3 _logger = logging.getLogger(__name__) @@ -94,6 +95,7 @@ def __init__(self, target_endpoint: str, credential_manager: 'CloudFoundryClient self.organizations = OrganizationManager(target_endpoint, credential_manager) self.service_instances = EntityManagerV3(target_endpoint, credential_manager, '/v3/service_instances') self.tasks = TaskManager(target_endpoint, credential_manager) + self.jobs = JobManagerV3(target_endpoint, credential_manager) class CloudFoundryClient(CredentialManager): diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index 7f8ef13..34f1ff9 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -181,6 +181,12 @@ def get(self, entity_id: str, *extra_paths) -> Entity: def _read_response(self, response: Response, entity_type: Optional[ENTITY_TYPE]) -> Union[JsonObject, Entity]: result = response.json(object_pairs_hook=JsonObject) + if 'Location' in response.headers: + result['links']['job'] = { + "href": response.headers['Location'], + "method": "GET", + } + return self._entity(result, entity_type) @staticmethod diff --git a/main/cloudfoundry_client/v3/jobs.py b/main/cloudfoundry_client/v3/jobs.py new file mode 100644 index 0000000..4df3339 --- /dev/null +++ b/main/cloudfoundry_client/v3/jobs.py @@ -0,0 +1,33 @@ +import types +import polling2 + +from cloudfoundry_client.v3.entities import EntityManager, Entity + + +class JobTimeout(Exception): + pass + + +class JobManager(EntityManager): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(JobManager, self).__init__(target_endpoint, client, "/v3/jobs") + + def wait_for_job_completion( + self, + job_guid: str, + step: int = 1, + step_function: types.FunctionType = lambda step: min(step + step, 60), + poll_forever: bool = False, + timeout: int = 600, + ) -> Entity: + try: + return polling2.poll( + lambda: self.get(job_guid), + step=step, + step_function=step_function, + poll_forever=poll_forever, + timeout=timeout, + check_success=lambda job: job["state"] != "PROCESSING", + ) + except polling2.TimeoutException as e: + raise JobTimeout(e) diff --git a/requirements.txt b/requirements.txt index 132a176..c1e85c8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ protobuf==3.6.1 oauth2-client==1.2.1 websocket-client==0.54.0 PyYAML==5.3.1 +polling2==0.4.6 diff --git a/test/fixtures/v3/jobs/GET_{id}_complete_response.json b/test/fixtures/v3/jobs/GET_{id}_complete_response.json new file mode 100644 index 0000000..54aa460 --- /dev/null +++ b/test/fixtures/v3/jobs/GET_{id}_complete_response.json @@ -0,0 +1,14 @@ +{ + "guid": "b19ae525-cbd3-4155-b156-dc0c2a431b4c", + "created_at": "2016-10-19T20:25:04Z", + "updated_at": "2016-11-08T16:41:26Z", + "operation": "app.delete", + "state": "COMPLETE", + "links": { + "self": { + "href": "https://api.example.org/v3/jobs/b19ae525-cbd3-4155-b156-dc0c2a431b4c" + } + }, + "errors": [], + "warnings": [] + } \ No newline at end of file diff --git a/test/fixtures/v3/jobs/GET_{id}_failed_response.json b/test/fixtures/v3/jobs/GET_{id}_failed_response.json new file mode 100644 index 0000000..867a6a9 --- /dev/null +++ b/test/fixtures/v3/jobs/GET_{id}_failed_response.json @@ -0,0 +1,27 @@ +{ + "guid": "b19ae525-cbd3-4155-b156-dc0c2a431b4c", + "created_at": "2016-10-19T20:25:04Z", + "updated_at": "2016-11-08T16:41:26Z", + "operation": "app.delete", + "state": "FAILED", + "links": { + "self": { + "href": "https://api.example.org/v3/jobs/b19ae525-cbd3-4155-b156-dc0c2a431b4c" + }, + "app": { + "href": "https://api.example.org/v3/apps/7b34f1cf-7e73-428a-bb5a-8a17a8058396" + } + }, + "errors": [ + { + "code": 10008, + "title": "CF-UnprocessableEntity", + "detail": "something went wrong" + } + ], + "warnings": [ + { + "detail": "warning! warning!" + } + ] +} \ No newline at end of file diff --git a/test/fixtures/v3/jobs/GET_{id}_processing_response.json b/test/fixtures/v3/jobs/GET_{id}_processing_response.json new file mode 100644 index 0000000..3fac0ea --- /dev/null +++ b/test/fixtures/v3/jobs/GET_{id}_processing_response.json @@ -0,0 +1,14 @@ +{ + "guid": "b19ae525-cbd3-4155-b156-dc0c2a431b4c", + "created_at": "2016-10-19T20:25:04Z", + "updated_at": "2016-11-08T16:41:26Z", + "operation": "app.delete", + "state": "PROCESSING", + "links": { + "self": { + "href": "https://api.example.org/v3/jobs/b19ae525-cbd3-4155-b156-dc0c2a431b4c" + } + }, + "errors": [], + "warnings": [] + } \ No newline at end of file diff --git a/test/v3/test_buildpacks.py b/test/v3/test_buildpacks.py index 585c0cd..523c595 100644 --- a/test/v3/test_buildpacks.py +++ b/test/v3/test_buildpacks.py @@ -1,7 +1,7 @@ import sys import unittest from http import HTTPStatus -from unittest.mock import patch +from unittest.mock import patch, mock_open import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase @@ -113,3 +113,21 @@ def test_main_get_buildpack(self): 'v3', 'buildpacks', 'GET_{id}_response.json') main.main() self.client.get.assert_called_with(self.client.get.return_value.url) + + def test_upload_buildpack(self): + self.client.post.return_value = self.mock_response( + '/v3/buildpacks/buildpack_id/upload', + HTTPStatus.ACCEPTED, + { + "Location": "https://somewhere.org/v3/jobs/job_guid" + }, + 'v3', 'buildpacks', 'POST_response.json') + + with patch('cloudfoundry_client.v3.entities.open', mock_open(read_data='ZipContent')) as m: + result = self.client.v3.buildpacks.upload('buildpack_id', '/path/to/buildpack.zip',) + + self.client.post.assert_called_with(self.client.post.return_value.url, + files={'bits': ('/path/to/buildpack.zip', m.return_value)}, + json=None) + + self.assertIsNotNone(result) diff --git a/test/v3/test_jobs.py b/test/v3/test_jobs.py new file mode 100644 index 0000000..e62ab29 --- /dev/null +++ b/test/v3/test_jobs.py @@ -0,0 +1,115 @@ +import unittest +from http import HTTPStatus +from unittest.mock import patch, call +from abstract_test_case import AbstractTestCase +from cloudfoundry_client.v3.jobs import JobTimeout + + +class TestJobs(unittest.TestCase, AbstractTestCase): + @classmethod + def setUpClass(cls): + cls.mock_client_class() + + def setUp(self): + self.build_client() + + def test_get(self): + self.client.get.return_value = self.mock_response( + "/v3/jobs/job_id", + HTTPStatus.OK, + None, + "v3", + "jobs", + "GET_{id}_processing_response.json", + ) + job = self.client.v3.jobs.get("job_id") + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertIsNotNone(job) + + @patch("time.sleep", return_value=None) + def test_wait_for_job_completion(self, sleepmock): + self.client.get.side_effect = [ + self.mock_response( + "/v3/jobs/job_id", + HTTPStatus.OK, + None, + "v3", + "jobs", + "GET_{id}_processing_response.json", + ), + self.mock_response( + "/v3/jobs/job_id", + HTTPStatus.OK, + None, + "v3", + "jobs", + "GET_{id}_processing_response.json", + ), + self.mock_response( + "/v3/jobs/job_id", + HTTPStatus.OK, + None, + "v3", + "jobs", + "GET_{id}_complete_response.json", + ), + ] + + job = self.client.v3.jobs.wait_for_job_completion("job_id") + + assert self.client.get.call_count == 3 + self.assertIsNotNone(job) + + def test_wait_for_job_completion_does_exponential_backoff(self): + self.client.get.side_effect = [ + self.mock_response( + "/v3/jobs/job_id", + HTTPStatus.OK, + None, + "v3", + "jobs", + "GET_{id}_processing_response.json", + ), + self.mock_response( + "/v3/jobs/job_id", + HTTPStatus.OK, + None, + "v3", + "jobs", + "GET_{id}_processing_response.json", + ), + self.mock_response( + "/v3/jobs/job_id", + HTTPStatus.OK, + None, + "v3", + "jobs", + "GET_{id}_processing_response.json", + ), + self.mock_response( + "/v3/jobs/job_id", + HTTPStatus.OK, + None, + "v3", + "jobs", + "GET_{id}_complete_response.json", + ), + ] + + with patch("time.sleep", return_value=None) as m: + self.client.v3.jobs.wait_for_job_completion("job_id") + m.assert_has_calls([call(1), call(2), call(4)]) + + @patch("time.sleep", return_value=None) + def test_wait_for_job_completion_has_timeout(self, sleepmock): + self.client.get.return_value = self.mock_response( + "/v3/jobs/job_id", + HTTPStatus.OK, + None, + "v3", + "jobs", + "GET_{id}_processing_response.json", + ) + + with self.assertRaises(JobTimeout): + self.client.v3.jobs.wait_for_job_completion("job_id", timeout=0.0001) From 6e0012fcb5f53a3f103769c9b41bd1fa62ffe8c0 Mon Sep 17 00:00:00 2001 From: FloThinksPi Date: Fri, 19 Feb 2021 17:11:35 +0100 Subject: [PATCH 061/264] Add config for flak8 and black for linting/formatting Also included the linter in the github actions pipeline --- .flake8 | 7 +++++++ .github/workflows/pythonpackage.yml | 9 +++++---- dev-requirements.txt | 2 ++ pyproject.toml | 7 +++++++ 4 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 .flake8 create mode 100644 dev-requirements.txt create mode 100644 pyproject.toml diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..5099cb2 --- /dev/null +++ b/.flake8 @@ -0,0 +1,7 @@ +[flake8] +ignore = E203, W503 +max-line-length = 130 +max-complexity = 16 +buildins = CloudFoundryClient +exclude = vendors,.git,.github,main/cloudfoundry_client/dropsonde + diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 127533c..f3deb76 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -9,7 +9,7 @@ jobs: strategy: max-parallel: 1 matrix: - python-version: [3.6, 3.7] + python-version: [3.6, 3.7, 3.8, 3.9] steps: - uses: actions/checkout@v1 @@ -21,7 +21,8 @@ jobs: run: | python -m pip install --upgrade pip pip install -r requirements.txt + pip install -r dev-requirements.txt - name: Launch tests - run: | - python setup.py test - + run: python setup.py test + - name: Launch Linting + run: flake8 --show-source --statistics diff --git a/dev-requirements.txt b/dev-requirements.txt new file mode 100644 index 0000000..d6762e6 --- /dev/null +++ b/dev-requirements.txt @@ -0,0 +1,2 @@ +black==20.8b1 +flake8==3.8.4 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..44b812c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,7 @@ +[tool.black] +line-length = 130 +exclude = ''' +^/( + (main\/cloudfoundry_client\/dropsonde.*) +)/ +''' \ No newline at end of file From e7a2228abf9a409d58b8e9a56a1042c8bab7626b Mon Sep 17 00:00:00 2001 From: FloThinksPi Date: Fri, 19 Feb 2021 17:12:24 +0100 Subject: [PATCH 062/264] Fix all findings of flak8 and let black format the files --- integration/config_test.py | 45 ++- integration/test_applications.py | 50 ++- integration/test_buildpacks.py | 2 +- integration/test_loggregator.py | 4 +- integration/test_navigation.py | 24 +- integration/test_organizations.py | 6 +- integration/test_routes.py | 2 +- integration/test_service_bindings.py | 26 +- integration/test_service_brokers.py | 8 +- integration/test_service_instances.py | 20 +- integration/test_service_keys.py | 23 +- integration/test_service_plans.py | 7 +- integration/test_services.py | 9 +- integration/test_spaces.py | 8 +- main/cloudfoundry_client/client.py | 172 +++++---- main/cloudfoundry_client/doppler/client.py | 49 +-- .../doppler/websocket_envelope_reader.py | 18 +- main/cloudfoundry_client/errors.py | 6 +- main/cloudfoundry_client/json_object.py | 2 +- .../main/apps_command_domain.py | 100 +++--- .../main/command_domain.py | 142 +++++--- main/cloudfoundry_client/main/main.py | 254 ++++++++------ .../main/operation_commands.py | 10 +- .../main/tasks_command_domain.py | 48 ++- .../networking/entities.py | 72 ++-- .../networking/v1/external/policies.py | 37 +- .../operations/push/cf_ignore.py | 35 +- .../operations/push/file_helper.py | 15 +- .../operations/push/push.py | 331 ++++++++++-------- .../operations/push/validation/manifest.py | 103 +++--- main/cloudfoundry_client/request_object.py | 2 +- main/cloudfoundry_client/rlpgateway/client.py | 4 +- main/cloudfoundry_client/v2/apps.py | 160 +++++---- main/cloudfoundry_client/v2/buildpacks.py | 9 +- main/cloudfoundry_client/v2/entities.py | 86 +++-- main/cloudfoundry_client/v2/events.py | 9 +- main/cloudfoundry_client/v2/jobs.py | 9 +- main/cloudfoundry_client/v2/resources.py | 9 +- main/cloudfoundry_client/v2/routes.py | 13 +- .../v2/service_bindings.py | 16 +- .../cloudfoundry_client/v2/service_brokers.py | 37 +- .../v2/service_instances.py | 58 +-- main/cloudfoundry_client/v2/service_keys.py | 11 +- .../v2/service_plan_visibilities.py | 17 +- main/cloudfoundry_client/v2/service_plans.py | 14 +- main/cloudfoundry_client/v3/apps.py | 13 +- main/cloudfoundry_client/v3/buildpacks.py | 68 ++-- main/cloudfoundry_client/v3/domains.py | 74 ++-- main/cloudfoundry_client/v3/entities.py | 103 +++--- main/cloudfoundry_client/v3/feature_flags.py | 14 +- .../v3/isolation_segments.py | 78 +++-- main/cloudfoundry_client/v3/organizations.py | 59 ++-- main/cloudfoundry_client/v3/spaces.py | 29 +- main/cloudfoundry_client/v3/tasks.py | 33 +- requirements.txt | 2 + setup.py | 95 ++--- test/abstract_test_case.py | 85 +++-- test/networking/v1/external/test_policies.py | 64 ++-- test/operations/push/test_cf_ignore.py | 39 +-- test/operations/push/test_file_helper.py | 24 +- test/operations/push/test_push.py | 46 +-- .../push/validation/test_manifest_reader.py | 121 ++++--- test/test_client.py | 160 +++++---- test/test_request_object.py | 13 +- test/v2/test_apps.py | 316 ++++++++--------- test/v2/test_buildpacks.py | 26 +- test/v2/test_doppler.py | 23 +- test/v2/test_entities.py | 121 +++---- test/v2/test_events.py | 19 +- test/v2/test_organizations.py | 35 +- test/v2/test_routes.py | 71 ++-- test/v2/test_service_bindings.py | 102 +++--- test/v2/test_service_brokers.py | 86 ++--- test/v2/test_service_instances.py | 235 ++++++------- test/v2/test_service_keys.py | 105 +++--- test/v2/test_service_plan_visibilities.py | 126 ++++--- test/v2/test_service_plans.py | 84 ++--- test/v2/test_services.py | 65 ++-- test/v2/test_spaces.py | 72 ++-- test/v3/test_apps.py | 83 ++--- test/v3/test_buildpacks.py | 121 +++---- test/v3/test_domains.py | 158 ++++----- test/v3/test_feature_flags.py | 38 +- test/v3/test_isolation_segments.py | 130 +++---- test/v3/test_organizations.py | 157 ++++----- test/v3/test_service_instances.py | 20 +- test/v3/test_spaces.py | 116 +++--- test/v3/test_tasks.py | 86 +++-- 88 files changed, 2878 insertions(+), 2719 deletions(-) diff --git a/integration/config_test.py b/integration/config_test.py index b80cc9f..b33bfde 100644 --- a/integration/config_test.py +++ b/integration/config_test.py @@ -13,29 +13,28 @@ def _init_logging(): HTTPConnection.debuglevel = 1 - logging.basicConfig(level=logging.DEBUG, - format='%(levelname)5s - %(name)s - %(message)s') + logging.basicConfig(level=logging.DEBUG, format="%(levelname)5s - %(name)s - %(message)s") requests_log = logging.getLogger("requests.packages.urllib3") requests_log.setLevel(logging.DEBUG) requests_log.propagate = True def get_resource_dir(): - result = os.path.join(os.path.dirname(__file__), 'resources') + result = os.path.join(os.path.dirname(__file__), "resources") if not (os.path.exists(result) and os.path.isdir(result)): - raise IOError('Directory %s must exist.' % result) + raise IOError("Directory %s must exist." % result) return result def get_resource(file_name): result = os.path.join(get_resource_dir(), file_name) if not (os.path.exists(result) and os.path.isfile(result) and os.access(result, os.R_OK)): - raise IOError('File %s must exist.' % result) + raise IOError("File %s must exist." % result) return result def get_build_dir(): - result = os.path.join(os.path.dirname(__file__), '..', 'dist') + result = os.path.join(os.path.dirname(__file__), "..", "dist") return result @@ -44,33 +43,31 @@ def build_client_from_configuration(): if _client is None: _init_logging() cfg = ConfigParser() - cfg.read(get_resource('test.properties')) + cfg.read(get_resource("test.properties")) proxy = None try: - http = cfg.get('proxy', 'http') - https = cfg.get('proxy', 'https') + http = cfg.get("proxy", "http") + https = cfg.get("proxy", "https") proxy = dict(http=http, https=https) except (NoSectionError, NoOptionError): pass skip_verification = False try: - skip_verification_str = cfg.get('service', 'skip_ssl_verification') - skip_verification = skip_verification_str.lower() == 'true' + skip_verification_str = cfg.get("service", "skip_ssl_verification") + skip_verification = skip_verification_str.lower() == "true" except (NoSectionError, NoOptionError): pass - client = CloudFoundryClient(cfg.get('service', 'target_endpoint'), proxy=proxy, - skip_verification=skip_verification) - client.init_with_user_credentials(cfg.get('authentification', 'login'), - cfg.get('authentification', 'password')) - client.org_guid = cfg.get('test_data', 'org_guid') - client.space_guid = cfg.get('test_data', 'space_guid') - client.app_guid = cfg.get('test_data', 'app_guid') - client.log_app_guid = cfg.get('test_data', 'log_app_guid') - client.service_guid = cfg.get('test_data', 'service_guid') - client.service_name = cfg.get('test_data', 'service_name') - client.plan_guid = cfg.get('test_data', 'plan_guid') - client.creation_parameters = eval(cfg.get('test_data', 'creation_parameters')) - client.update_parameters = eval(cfg.get('test_data', 'update_parameters')) + client = CloudFoundryClient(cfg.get("service", "target_endpoint"), proxy=proxy, skip_verification=skip_verification) + client.init_with_user_credentials(cfg.get("authentification", "login"), cfg.get("authentification", "password")) + client.org_guid = cfg.get("test_data", "org_guid") + client.space_guid = cfg.get("test_data", "space_guid") + client.app_guid = cfg.get("test_data", "app_guid") + client.log_app_guid = cfg.get("test_data", "log_app_guid") + client.service_guid = cfg.get("test_data", "service_guid") + client.service_name = cfg.get("test_data", "service_name") + client.plan_guid = cfg.get("test_data", "plan_guid") + client.creation_parameters = eval(cfg.get("test_data", "creation_parameters")) + client.update_parameters = eval(cfg.get("test_data", "update_parameters")) _client = client return _client diff --git a/integration/test_applications.py b/integration/test_applications.py index a65be31..e717c8a 100644 --- a/integration/test_applications.py +++ b/integration/test_applications.py @@ -15,54 +15,52 @@ def test_list(self): cpt = 0 client = build_client_from_configuration() for application in client.v2.apps.list(space_guid=client.space_guid): - _logger.debug('- %s' % application['entity']['name']) + _logger.debug("- %s" % application["entity"]["name"]) if cpt == 0: - _logger.debug('- %s' % application['metadata']['guid']) - self.assertIsNotNone(client.v2.apps.get_first(space_guid=client.space_guid, - name=application['entity']['name'])) - self.assertIsNotNone(client.v2.apps.get(application['metadata']['guid'])) + _logger.debug("- %s" % application["metadata"]["guid"]) + self.assertIsNotNone(client.v2.apps.get_first(space_guid=client.space_guid, name=application["entity"]["name"])) + self.assertIsNotNone(client.v2.apps.get(application["metadata"]["guid"])) try: - client.v2.apps.get('%s-0' % application['metadata']['guid']) - self.fail('Should not have been found') + client.v2.apps.get("%s-0" % application["metadata"]["guid"]) + self.fail("Should not have been found") except InvalidStatusCode as e: self.assertEquals(e.status_code, HTTPStatus.NOT_FOUND) try: - instances = client.v2.apps.get_instances(application['metadata']['guid']) + instances = client.v2.apps.get_instances(application["metadata"]["guid"]) self.assertIsNotNone(instances) - self.assertEquals(len(instances), application['entity']['instances']) - _logger.debug('instances = %s', json.dumps(instances)) + self.assertEquals(len(instances), application["entity"]["instances"]) + _logger.debug("instances = %s", json.dumps(instances)) except InvalidStatusCode as e: - #instance is stopped + # instance is stopped self.assertEquals(e.status_code, HTTPStatus.BAD_REQUEST) self.assertIsInstance(e.body, dict) - self.assertEqual(e.body.get('error_code'), 'CF-InstancesError') + self.assertEqual(e.body.get("error_code"), "CF-InstancesError") try: - stats = client.v2.apps.get_stats(application['metadata']['guid']) + stats = client.v2.apps.get_stats(application["metadata"]["guid"]) self.assertIsNotNone(stats) - self.assertEquals(len(stats), application['entity']['instances']) - self.assertEquals(len(stats), application['entity']['instances']) - _logger.debug('stats = %s', json.dumps(stats)) + self.assertEquals(len(stats), application["entity"]["instances"]) + self.assertEquals(len(stats), application["entity"]["instances"]) + _logger.debug("stats = %s", json.dumps(stats)) except InvalidStatusCode as e: # instance is stopped self.assertEquals(e.status_code, HTTPStatus.BAD_REQUEST) self.assertIsInstance(e.body, dict) - self.assertEqual(e.body.get('error_code'), 'CF-AppStoppedStatsError') - env = client.v2.apps.get_env(application['metadata']['guid']) + self.assertEqual(e.body.get("error_code"), "CF-AppStoppedStatsError") + env = client.v2.apps.get_env(application["metadata"]["guid"]) self.assertIsNotNone(env) - self.assertIsNotNone(env.get('application_env_json', None)) - self.assertIsNotNone(env['application_env_json'].get('VCAP_APPLICATION', None)) - self.assertGreater(len(env['application_env_json']['VCAP_APPLICATION'].get('application_uris', [])), 0) - _logger.debug('env = %s', json.dumps(env)) + self.assertIsNotNone(env.get("application_env_json", None)) + self.assertIsNotNone(env["application_env_json"].get("VCAP_APPLICATION", None)) + self.assertGreater(len(env["application_env_json"]["VCAP_APPLICATION"].get("application_uris", [])), 0) + _logger.debug("env = %s", json.dumps(env)) cpt += 1 - _logger.debug('test applications list - %d found', cpt) + _logger.debug("test applications list - %d found", cpt) def test_start(self): client = build_client_from_configuration() - _logger.debug('start - %s', client.v2.apps.start(client.app_guid, async=False)) + _logger.debug("start - %s", client.v2.apps.start(client.app_guid, asynchronous=False)) def test_stop(self): client = build_client_from_configuration() - _logger.debug('stop - %s', client.v2.apps.stop(client.app_guid, async=False)) - + _logger.debug("stop - %s", client.v2.apps.stop(client.app_guid, asynchronous=False)) diff --git a/integration/test_buildpacks.py b/integration/test_buildpacks.py index 14ae5ee..3953bf9 100644 --- a/integration/test_buildpacks.py +++ b/integration/test_buildpacks.py @@ -10,4 +10,4 @@ class TestBuildpacks(unittest.TestCase): def test_list(self): client = build_client_from_configuration() for buildpack in client.v2.buildpacks.list(): - _logger.debug(' %s' % buildpack.json()) + _logger.debug(" %s" % buildpack.json()) diff --git a/integration/test_loggregator.py b/integration/test_loggregator.py index 1dc28ae..7d4b514 100644 --- a/integration/test_loggregator.py +++ b/integration/test_loggregator.py @@ -11,5 +11,5 @@ def test_recent(self): cpt = 0 for log_message in client.loggregator.get_recent(client.log_app_guid): cpt += 1 - _logger.debug('read %s', str(log_message)) - _logger.debug('read %d', cpt) + _logger.debug("read %s", str(log_message)) + _logger.debug("read %d", cpt) diff --git a/integration/test_navigation.py b/integration/test_navigation.py index 03cdea3..b74ab41 100644 --- a/integration/test_navigation.py +++ b/integration/test_navigation.py @@ -10,15 +10,15 @@ class TestNavigation(unittest.TestCase): def test_all(self): client = build_client_from_configuration() for organization in client.v2.organizations: - if organization['metadata']['guid'] == client.org_guid: + if organization["metadata"]["guid"] == client.org_guid: for space in organization.spaces(): - if space['metadata']['guid'] == client.space_guid: + if space["metadata"]["guid"] == client.space_guid: organization_reloaded = space.organization() - self.assertEqual(organization['metadata']['guid'], organization_reloaded['metadata']['guid']) + self.assertEqual(organization["metadata"]["guid"], organization_reloaded["metadata"]["guid"]) for application in space.apps(): - if application['metadata']['guid'] == client.app_guid: + if application["metadata"]["guid"] == client.app_guid: space_reloaded = application.space() - self.assertEqual(space['metadata']['guid'], space_reloaded['metadata']['guid']) + self.assertEqual(space["metadata"]["guid"], space_reloaded["metadata"]["guid"]) application.start() application.stats() application.instances() @@ -32,21 +32,23 @@ def test_all(self): application.stop() for service_instance in space.service_instances(): space_reloaded = service_instance.space() - self.assertEqual(space['metadata']['guid'], space_reloaded['metadata']['guid']) + self.assertEqual(space["metadata"]["guid"], space_reloaded["metadata"]["guid"]) for service_binding in service_instance.service_bindings(): service_instance_reloaded = service_binding.service_instance() - self.assertEqual(service_instance['metadata']['guid'], - service_instance_reloaded['metadata']['guid']) + self.assertEqual( + service_instance["metadata"]["guid"], service_instance_reloaded["metadata"]["guid"] + ) service_binding.app() break for route in service_instance.routes(): service_instance_reloaded = route.service_instance() - self.assertEqual(service_instance['metadata']['guid'], - service_instance_reloaded['metadata']['guid']) + self.assertEqual( + service_instance["metadata"]["guid"], service_instance_reloaded["metadata"]["guid"] + ) for _ in route.apps(): break space_reloaded = route.space() - self.assertEqual(space['metadata']['guid'], space_reloaded['metadata']['guid']) + self.assertEqual(space["metadata"]["guid"], space_reloaded["metadata"]["guid"]) break service_plan = service_instance.service_plan() for _ in service_plan.service_instances(): diff --git a/integration/test_organizations.py b/integration/test_organizations.py index 61cc3ae..5876434 100644 --- a/integration/test_organizations.py +++ b/integration/test_organizations.py @@ -11,10 +11,10 @@ def test_list(self): client = build_client_from_configuration() for organization in client.v2.organizations.list(): if cpt == 0: - organization = client.v2.organizations.get(organization['metadata']['guid']) + organization = client.v2.organizations.get(organization["metadata"]["guid"]) self.assertIsNotNone(organization) - organization = client.v2.organizations.get_first(name=organization['entity']['name']) + organization = client.v2.organizations.get_first(name=organization["entity"]["name"]) self.assertIsNotNone(organization) _logger.debug(organization.json()) cpt += 1 - _logger.debug('test organization list - %d found', cpt) + _logger.debug("test organization list - %d found", cpt) diff --git a/integration/test_routes.py b/integration/test_routes.py index 0e7c60f..9bbf0f9 100644 --- a/integration/test_routes.py +++ b/integration/test_routes.py @@ -10,4 +10,4 @@ class TestRoutes(unittest.TestCase): def test_list(self): client = build_client_from_configuration() for route in client.v2.routes.list(): - _logger.debug(' %s' % route.json()) + _logger.debug(" %s" % route.json()) diff --git a/integration/test_service_bindings.py b/integration/test_service_bindings.py index 7cd9017..fdd10b5 100644 --- a/integration/test_service_bindings.py +++ b/integration/test_service_bindings.py @@ -11,19 +11,20 @@ class TestServiceBinding(unittest.TestCase): def test_create_bind_unbind_delete(self): client = build_client_from_configuration() try: - instance = client.v2.service_instances.create(client.space_guid, 'test_name', client.plan_guid, - client.creation_parameters) - except: + instance = client.v2.service_instances.create( + client.space_guid, "test_name", client.plan_guid, client.creation_parameters + ) + except: # noqa: E722 return try: - binding = client.v2.service_bindings.create(client.app_guid, instance['metadata']['guid']) + binding = client.v2.service_bindings.create(client.app_guid, instance["metadata"]["guid"]) _logger.debug(binding.json()) - client.v2.service_bindings.remove(binding['metadata']['guid']) + client.v2.service_bindings.remove(binding["metadata"]["guid"]) _logger.debug("binding deleted") finally: try: - client.v2.service_instances.remove(instance['metadata']['guid']) - except: + client.v2.service_instances.remove(instance["metadata"]["guid"]) + except: # noqa: E722 pass def test_get(self): @@ -33,10 +34,9 @@ def test_get(self): if cpt == 0: _logger.debug(binding) self.assertIsNotNone( - client.v2.service_bindings.get_first(service_instance_guid=binding['entity']['service_instance_guid'])) - self.assertIsNotNone( - client.v2.service_bindings.get_first(app_guid=binding['entity']['app_guid'])) - self.assertIsNotNone( - client.v2.service_bindings.get(binding['metadata']['guid'])) + client.v2.service_bindings.get_first(service_instance_guid=binding["entity"]["service_instance_guid"]) + ) + self.assertIsNotNone(client.v2.service_bindings.get_first(app_guid=binding["entity"]["app_guid"])) + self.assertIsNotNone(client.v2.service_bindings.get(binding["metadata"]["guid"])) cpt += 1 - _logger.debug('test_get - %d found', cpt) + _logger.debug("test_get - %d found", cpt) diff --git a/integration/test_service_brokers.py b/integration/test_service_brokers.py index 6379149..6ea4f4a 100644 --- a/integration/test_service_brokers.py +++ b/integration/test_service_brokers.py @@ -12,10 +12,8 @@ def test_list(self): client = build_client_from_configuration() for broker in client.v2.service_brokers.list(): if cpt == 0: - self.assertIsNotNone( - client.v2.service_brokers.get_first(space_guid=broker['entity']['space_guid'])) - self.assertIsNotNone( - client.v2.service_brokers.get(broker['metadata']['guid'])) + self.assertIsNotNone(client.v2.service_brokers.get_first(space_guid=broker["entity"]["space_guid"])) + self.assertIsNotNone(client.v2.service_brokers.get(broker["metadata"]["guid"])) cpt += 1 _logger.debug(broker) - _logger.debug('test broker list - %d found', cpt) + _logger.debug("test broker list - %d found", cpt) diff --git a/integration/test_service_instances.py b/integration/test_service_instances.py index cac2200..4c31c44 100644 --- a/integration/test_service_instances.py +++ b/integration/test_service_instances.py @@ -9,24 +9,20 @@ class TestServiceInstances(unittest.TestCase): def test_create_update_delete(self): client = build_client_from_configuration() - result = client.v2.service_instances.create(client.space_guid, 'test_name', client.plan_guid, - client.creation_parameters) + result = client.v2.service_instances.create(client.space_guid, "test_name", client.plan_guid, client.creation_parameters) if len(client.update_parameters) > 0: - client.v2.service_instances.update(result['metadata']['guid'], client.update_parameters) + client.v2.service_instances.update(result["metadata"]["guid"], client.update_parameters) else: - _logger.warning('update test skipped') - client.v2.service_instances.remove(result['metadata']['guid']) + _logger.warning("update test skipped") + client.v2.service_instances.remove(result["metadata"]["guid"]) def test_get(self): client = build_client_from_configuration() cpt = 0 for instance in client.v2.service_instances.list(): if cpt == 0: - self.assertIsNotNone( - client.v2.service_instances.get_first(space_guid=instance['entity']['space_guid'])) - self.assertIsNotNone( - client.v2.service_instances.get(instance['metadata']['guid'])) - self.assertIsNotNone( - client.v2.service_instances.list_permissions(instance['metadata']['guid'])) + self.assertIsNotNone(client.v2.service_instances.get_first(space_guid=instance["entity"]["space_guid"])) + self.assertIsNotNone(client.v2.service_instances.get(instance["metadata"]["guid"])) + self.assertIsNotNone(client.v2.service_instances.list_permissions(instance["metadata"]["guid"])) cpt += 1 - _logger.debug('test_get - %d found', cpt) + _logger.debug("test_get - %d found", cpt) diff --git a/integration/test_service_keys.py b/integration/test_service_keys.py index f092ed0..52ce196 100644 --- a/integration/test_service_keys.py +++ b/integration/test_service_keys.py @@ -10,19 +10,20 @@ class TestServiceKeys(unittest.TestCase): def test_create_delete(self): client = build_client_from_configuration() try: - instance = client.v2.service_instances.create(client.space_guid, 'test_name', client.plan_guid, - client.creation_parameters) - except: + instance = client.v2.service_instances.create( + client.space_guid, "test_name", client.plan_guid, client.creation_parameters + ) + except: # noqa: E722 return try: - service_key = client.v2.service_keys.create(instance['metadata']['guid'], 'test_key_name') + service_key = client.v2.service_keys.create(instance["metadata"]["guid"], "test_key_name") _logger.debug(service_key.json()) - client.v2.service_keys.remove(service_key['metadata']['guid']) + client.v2.service_keys.remove(service_key["metadata"]["guid"]) _logger.debug("service key deleted") finally: try: - client.v2.service_instances.remove(instance['metadata']['guid']) - except: + client.v2.service_instances.remove(instance["metadata"]["guid"]) + except: # noqa: E722 pass def test_get(self): @@ -31,8 +32,8 @@ def test_get(self): for service_key in client.v2.service_keys.list(): if cpt == 0: self.assertIsNotNone( - client.v2.service_keys.get_first(service_instance_guid=service_key['entity']['service_instance_guid'])) - self.assertIsNotNone( - client.v2.service_keys.get(service_key['metadata']['guid'])) + client.v2.service_keys.get_first(service_instance_guid=service_key["entity"]["service_instance_guid"]) + ) + self.assertIsNotNone(client.v2.service_keys.get(service_key["metadata"]["guid"])) cpt += 1 - _logger.debug('test_get - %d found', cpt) + _logger.debug("test_get - %d found", cpt) diff --git a/integration/test_service_plans.py b/integration/test_service_plans.py index 09b2f75..97ee56b 100644 --- a/integration/test_service_plans.py +++ b/integration/test_service_plans.py @@ -10,8 +10,7 @@ class TestServicePlan(unittest.TestCase): def test_list_instance_for_plan(self): client = build_client_from_configuration() for instance in client.v2.service_plans.list_instances(client.plan_guid, space_guid=client.space_guid): - _logger.debug('test_list_instance_for_plan - %s -%s', instance['metadata']['guid'], - instance['entity']['name']) + _logger.debug("test_list_instance_for_plan - %s -%s", instance["metadata"]["guid"], instance["entity"]["name"]) def test_list_by_broker(self): cpt = 0 @@ -20,7 +19,7 @@ def test_list_by_broker(self): if cpt == 0: _logger.debug(plan.json()) cpt += 1 - _logger.debug('test plan list - %d found', cpt) + _logger.debug("test plan list - %d found", cpt) def test_list(self): cpt = 0 @@ -28,4 +27,4 @@ def test_list(self): for plan in client.v2.service_plans.list(): _logger.debug(plan.json()) cpt += 1 - _logger.debug('test plan list - %d found', cpt) + _logger.debug("test plan list - %d found", cpt) diff --git a/integration/test_services.py b/integration/test_services.py index 28e2fc5..038cbe6 100644 --- a/integration/test_services.py +++ b/integration/test_services.py @@ -10,13 +10,10 @@ def test_list_services(self): cpt = 0 client = build_client_from_configuration() for service in client.v2.services.list(): - _logger.debug('- %s' % service['entity']['label']) + _logger.debug("- %s" % service["entity"]["label"]) if cpt == 0: - service = client.v2.services.get_first(label=service['entity']['label']) + service = client.v2.services.get_first(label=service["entity"]["label"]) self.assertIsNotNone(service) cpt += 1 - _logger.debug('test service list - %d found', cpt) - - - + _logger.debug("test service list - %d found", cpt) diff --git a/integration/test_spaces.py b/integration/test_spaces.py index de0e51f..5d791c0 100644 --- a/integration/test_spaces.py +++ b/integration/test_spaces.py @@ -11,11 +11,11 @@ def test_list(self): cpt = 0 client = build_client_from_configuration() for space in client.v2.spaces.list(organization_guid=client.org_guid): - _logger.debug(' - %s' % space['entity']['name']) + _logger.debug(" - %s" % space["entity"]["name"]) if cpt == 0: - space = client.v2.spaces.get(space['metadata']['guid']) + space = client.v2.spaces.get(space["metadata"]["guid"]) self.assertIsNotNone(space) - space = client.v2.spaces.get_first(organization_guid=client.org_guid, name=space['entity']['name']) + space = client.v2.spaces.get_first(organization_guid=client.org_guid, name=space["entity"]["name"]) self.assertIsNotNone(space) cpt += 1 - _logger.debug('test spaces list - %d found', cpt) + _logger.debug("test spaces list - %d found", cpt) diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 730146c..7d8de98 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -37,11 +37,14 @@ class Info: - def __init__(self, api_v2_version: str, - authorization_endpoint: str, - api_endpoint: str, - doppler_endpoint: Optional[str], - log_stream_endpoint: Optional[str]): + def __init__( + self, + api_v2_version: str, + authorization_endpoint: str, + api_endpoint: str, + doppler_endpoint: Optional[str], + log_stream_endpoint: Optional[str], + ): self.api_v2_version = api_v2_version self.authorization_endpoint = authorization_endpoint self.api_endpoint = api_endpoint @@ -50,12 +53,12 @@ def __init__(self, api_v2_version: str, class NetworkingV1External(object): - def __init__(self, target_endpoint: str, credential_manager: 'CloudFoundryClient'): + def __init__(self, target_endpoint: str, credential_manager: "CloudFoundryClient"): self.policies = PolicyManager(target_endpoint, credential_manager) class V2(object): - def __init__(self, target_endpoint: str, credential_manager: 'CloudFoundryClient'): + def __init__(self, target_endpoint: str, credential_manager: "CloudFoundryClient"): self.apps = AppManagerV2(target_endpoint, credential_manager) self.buildpacks = BuildpackManagerV2(target_endpoint, credential_manager) self.jobs = JobManager(target_endpoint, credential_manager) @@ -67,24 +70,24 @@ def __init__(self, target_endpoint: str, credential_manager: 'CloudFoundryClient self.service_plans = ServicePlanManager(target_endpoint, credential_manager) # Default implementations self.event = EventManager(target_endpoint, credential_manager) - self.organizations = EntityManagerV2(target_endpoint, credential_manager, '/v2/organizations') - self.private_domains = EntityManagerV2(target_endpoint, credential_manager, '/v2/private_domains') + self.organizations = EntityManagerV2(target_endpoint, credential_manager, "/v2/organizations") + self.private_domains = EntityManagerV2(target_endpoint, credential_manager, "/v2/private_domains") self.routes = RouteManager(target_endpoint, credential_manager) - self.services = EntityManagerV2(target_endpoint, credential_manager, '/v2/services') - self.shared_domains = EntityManagerV2(target_endpoint, credential_manager, '/v2/shared_domains') - self.spaces = EntityManagerV2(target_endpoint, credential_manager, '/v2/spaces') - self.stacks = EntityManagerV2(target_endpoint, credential_manager, '/v2/stacks') - self.user_provided_service_instances = EntityManagerV2(target_endpoint, credential_manager, - '/v2/user_provided_service_instances') - self.security_groups = EntityManagerV2( - target_endpoint, credential_manager, '/v2/security_groups') - self.users = EntityManagerV2(target_endpoint, credential_manager, '/v2/users') + self.services = EntityManagerV2(target_endpoint, credential_manager, "/v2/services") + self.shared_domains = EntityManagerV2(target_endpoint, credential_manager, "/v2/shared_domains") + self.spaces = EntityManagerV2(target_endpoint, credential_manager, "/v2/spaces") + self.stacks = EntityManagerV2(target_endpoint, credential_manager, "/v2/stacks") + self.user_provided_service_instances = EntityManagerV2( + target_endpoint, credential_manager, "/v2/user_provided_service_instances" + ) + self.security_groups = EntityManagerV2(target_endpoint, credential_manager, "/v2/security_groups") + self.users = EntityManagerV2(target_endpoint, credential_manager, "/v2/users") # Resources implementation used by push operation self.resources = ResourceManager(target_endpoint, credential_manager) class V3(object): - def __init__(self, target_endpoint: str, credential_manager: 'CloudFoundryClient'): + def __init__(self, target_endpoint: str, credential_manager: "CloudFoundryClient"): self.apps = AppManagerV3(target_endpoint, credential_manager) self.buildpacks = BuildpackManagerV3(target_endpoint, credential_manager) self.domains = DomainManager(target_endpoint, credential_manager) @@ -92,13 +95,13 @@ def __init__(self, target_endpoint: str, credential_manager: 'CloudFoundryClient self.isolation_segments = IsolationSegmentManager(target_endpoint, credential_manager) self.spaces = SpaceManager(target_endpoint, credential_manager) self.organizations = OrganizationManager(target_endpoint, credential_manager) - self.service_instances = EntityManagerV3(target_endpoint, credential_manager, '/v3/service_instances') + self.service_instances = EntityManagerV3(target_endpoint, credential_manager, "/v3/service_instances") self.tasks = TaskManager(target_endpoint, credential_manager) class CloudFoundryClient(CredentialManager): - def __init__(self, target_endpoint: str, client_id: str = 'cf', client_secret: str = '', **kwargs): - """" + def __init__(self, target_endpoint: str, client_id: str = "cf", client_secret: str = "", **kwargs): + """ " :param target_endpoint :the target endpoint :param client_id: the client_id :param client_secret: the client secret @@ -114,37 +117,47 @@ def __init__(self, target_endpoint: str, client_id: str = 'cf', client_secret: s of an identity provider. Note that this identity provider must support the grant type password. See UAA API specifications """ - proxy = kwargs.get('proxy', dict(http='', https='')) - verify = kwargs.get('verify', True) - self.token_format = kwargs.get('token_format') - self.login_hint = kwargs.get('login_hint') - target_endpoint_trimmed = target_endpoint.rstrip('/') + proxy = kwargs.get("proxy", dict(http="", https="")) + verify = kwargs.get("verify", True) + self.token_format = kwargs.get("token_format") + self.login_hint = kwargs.get("login_hint") + target_endpoint_trimmed = target_endpoint.rstrip("/") info = self._get_info(target_endpoint_trimmed, proxy, verify=verify) - if not info.api_v2_version.startswith('2.'): - raise AssertionError('Only version 2 is supported for now. Found %s' % info.api_v2_version) - service_information = ServiceInformation(None, - '%s/oauth/token' % info.authorization_endpoint, - client_id, client_secret, [], verify) + if not info.api_v2_version.startswith("2."): + raise AssertionError("Only version 2 is supported for now. Found %s" % info.api_v2_version) + service_information = ServiceInformation( + None, "%s/oauth/token" % info.authorization_endpoint, client_id, client_secret, [], verify + ) super(CloudFoundryClient, self).__init__(service_information, proxies=proxy) self.v2 = V2(target_endpoint_trimmed, self) self.v3 = V3(target_endpoint_trimmed, self) - self._doppler = DopplerClient(info.doppler_endpoint, - self.proxies[ - 'http' if info.doppler_endpoint.startswith('ws://') else 'https'], - self.service_information.verify, - self) if info.doppler_endpoint is not None else None - self._rlpgateway = RLPGatewayClient(info.log_stream_endpoint, - self.proxies['https'], - self.service_information.verify, - self, - ) if info.log_stream_endpoint is not None else None + self._doppler = ( + DopplerClient( + info.doppler_endpoint, + self.proxies["http" if info.doppler_endpoint.startswith("ws://") else "https"], + self.service_information.verify, + self, + ) + if info.doppler_endpoint is not None + else None + ) + self._rlpgateway = ( + RLPGatewayClient( + info.log_stream_endpoint, + self.proxies["https"], + self.service_information.verify, + self, + ) + if info.log_stream_endpoint is not None + else None + ) self.networking_v1_external = NetworkingV1External(target_endpoint_trimmed, self) self.info = info @property def doppler(self) -> DopplerClient: if self._doppler is None: - raise NotImplementedError('No droppler endpoint for this instance') + raise NotImplementedError("No droppler endpoint for this instance") else: return self._doppler @@ -152,59 +165,60 @@ def doppler(self) -> DopplerClient: @property def rlpgateway(self): if self._rlpgateway is None: - raise NotImplementedError('No RLP gateway endpoint for this instance') + raise NotImplementedError("No RLP gateway endpoint for this instance") else: return self._rlpgateway @staticmethod def _get_info(target_endpoint: str, proxy: Optional[dict] = None, verify: bool = True) -> Info: - info_response = CloudFoundryClient._check_response(requests.get('%s/info' % target_endpoint, - proxies=proxy if proxy is not None else dict( - http='', https=''), - verify=verify)) + info_response = CloudFoundryClient._check_response( + requests.get( + "%s/info" % target_endpoint, proxies=proxy if proxy is not None else dict(http="", https=""), verify=verify + ) + ) info = info_response.json() - root_response = CloudFoundryClient._check_response(requests.get('%s/' % target_endpoint, - proxies=proxy if proxy is not None else dict( - http='', https=''), - verify=verify)) + root_response = CloudFoundryClient._check_response( + requests.get("%s/" % target_endpoint, proxies=proxy if proxy is not None else dict(http="", https=""), verify=verify) + ) root_info = root_response.json() - root_links = root_info['links'] - logging = root_links.get('logging') - log_stream = root_links.get('log_stream') - return Info(root_links['cloud_controller_v2']['meta']['version'], - info['authorization_endpoint'], - target_endpoint, - logging.get('href') if logging is not None else None, - log_stream.get('href') if log_stream is not None else None) + root_links = root_info["links"] + logging = root_links.get("logging") + log_stream = root_links.get("log_stream") + return Info( + root_links["cloud_controller_v2"]["meta"]["version"], + info["authorization_endpoint"], + target_endpoint, + logging.get("href") if logging is not None else None, + log_stream.get("href") if log_stream is not None else None, + ) @staticmethod def _is_token_expired(response: Response) -> bool: if response.status_code == HTTPStatus.UNAUTHORIZED.value: try: json_data = response.json() - invalid_token_error = 'CF-InvalidAuthToken' - if json_data.get('errors', None) is not None: # V3 error response - for error in json_data.get('errors'): - if error.get('code', 0) == 1000 and error.get('title', '') == invalid_token_error: - _logger.info('_is_token_v3_expired - true') + invalid_token_error = "CF-InvalidAuthToken" + if json_data.get("errors", None) is not None: # V3 error response + for error in json_data.get("errors"): + if error.get("code", 0) == 1000 and error.get("title", "") == invalid_token_error: + _logger.info("_is_token_v3_expired - true") return True - _logger.info('_is_token_v3_expired - false') + _logger.info("_is_token_v3_expired - false") return False else: # V2 error response - token_expired = json_data.get('code', 0) == 1000 \ - and json_data.get('error_code', '') == invalid_token_error - _logger.info('_is_token__v2_expired - %s' % str(token_expired)) + token_expired = json_data.get("code", 0) == 1000 and json_data.get("error_code", "") == invalid_token_error + _logger.info("_is_token__v2_expired - %s" % str(token_expired)) return token_expired - except Exception as _: + except Exception: # noqa: E722 return False else: return False @staticmethod def _token_request_headers(_) -> dict: - return dict(Accept='application/json') + return dict(Accept="application/json") def __getattr__(self, item): sub_attr = getattr(self.v2, item, None) @@ -216,20 +230,24 @@ def __getattr__(self, item): def _grant_password_request(self, login: str, password: str) -> dict: request = super(CloudFoundryClient, self)._grant_password_request(login, password) if self.token_format is not None: - request['token_format'] = self.token_format + request["token_format"] = self.token_format if self.login_hint is not None: - request['login_hint'] = self.login_hint + request["login_hint"] = self.login_hint return request def _grant_refresh_token_request(self, refresh_token: str) -> dict: request = super(CloudFoundryClient, self)._grant_refresh_token_request(refresh_token) if self.token_format is not None: - request['token_format'] = self.token_format + request["token_format"] = self.token_format return request def _grant_client_credentials_request(self) -> dict: - return dict(grant_type="client_credentials", scope=' '.join(self.service_information.scopes), - client_id=self.service_information.client_id, client_secret=self.service_information.client_secret) + return dict( + grant_type="client_credentials", + scope=" ".join(self.service_information.scopes), + client_id=self.service_information.client_id, + client_secret=self.service_information.client_secret, + ) def get(self, url: str, params: Optional[dict] = None, **kwargs) -> Response: response = super(CloudFoundryClient, self).get(url, params, **kwargs) @@ -258,6 +276,6 @@ def _check_response(response: Response) -> Response: else: try: body = response.json() - except ValueError as _: + except ValueError: body = response.text raise InvalidStatusCode(HTTPStatus(response.status_code), body) diff --git a/main/cloudfoundry_client/doppler/client.py b/main/cloudfoundry_client/doppler/client.py index 5f88289..ae16827 100644 --- a/main/cloudfoundry_client/doppler/client.py +++ b/main/cloudfoundry_client/doppler/client.py @@ -20,30 +20,33 @@ def __init__(self, doppler_endpoint: str, proxy: dict, verify_ssl: bool, credent self.proxy_host = None self.proxy_port = None self.ws_doppler_endpoint = doppler_endpoint - self.http_doppler_endpoint = re.sub('^ws', 'http', doppler_endpoint) + self.http_doppler_endpoint = re.sub("^ws", "http", doppler_endpoint) self.verify_ssl = verify_ssl self.credentials_manager = credentials_manager if proxy is not None and len(proxy) > 0: proxy_domain = urlparse(proxy).netloc - idx = proxy_domain.find(':') + idx = proxy_domain.find(":") if 0 < idx < len(proxy_domain) - 2: self.proxy_host = proxy_domain[:idx] - self.proxy_port = int(proxy_domain[idx + 1:]) + self.proxy_port = int(proxy_domain[idx + 1 :]) def recent_logs(self, app_guid: str) -> EnvelopeStream: - url = '%s/apps/%s/recentlogs' % (self.http_doppler_endpoint, app_guid) + url = "%s/apps/%s/recentlogs" % (self.http_doppler_endpoint, app_guid) response = self.credentials_manager.get(url, stream=True) boundary = DopplerClient._extract_boundary(response) - _logger.debug('Boundary: %s' % boundary) + _logger.debug("Boundary: %s" % boundary) for part in DopplerClient._read_multi_part_response(response, boundary): yield DopplerClient._parse_envelope(part) def stream_logs(self, app_guid: str) -> EnvelopeStream: - url = '%s/apps/%s/stream' % (self.ws_doppler_endpoint, app_guid) - with WebsocketFrameReader(url, - lambda: self.credentials_manager._access_token, - verify_ssl=self.verify_ssl, - proxy_host=self.proxy_host, proxy_port=self.proxy_port) as websocket: + url = "%s/apps/%s/stream" % (self.ws_doppler_endpoint, app_guid) + with WebsocketFrameReader( + url, + lambda: self.credentials_manager._access_token, + verify_ssl=self.verify_ssl, + proxy_host=self.proxy_host, + proxy_port=self.proxy_port, + ) as websocket: for message in websocket: yield DopplerClient._parse_envelope(message) @@ -55,24 +58,24 @@ def _parse_envelope(raw) -> Envelope: @staticmethod def _extract_boundary(response: Response) -> str: - content_type = response.headers['content-type'] - _logger.debug('content-type=%s' % content_type) - boundary_field = 'boundary=' + content_type = response.headers["content-type"] + _logger.debug("content-type=%s" % content_type) + boundary_field = "boundary=" idx = content_type.find(boundary_field) if idx == -1: _logger.debug(response.text) - raise InvalidLogResponseException('Cannot extract boundary in %s' % content_type) - boundary = content_type[idx + len(boundary_field):] - idx = boundary.find(' ') + raise InvalidLogResponseException("Cannot extract boundary in %s" % content_type) + boundary = content_type[idx + len(boundary_field) :] + idx = boundary.find(" ") if idx != -1: boundary = boundary[:idx] return boundary @staticmethod def _read_multi_part_response(iterable, boundary): - remaining = '' - boundary_header = bytes('--%s' % boundary, 'UTF-8') - end_of_line = bytes('\r\n', 'UTF-8') + remaining = "" + boundary_header = bytes("--%s" % boundary, "UTF-8") + end_of_line = bytes("\r\n", "UTF-8") cpt_read = 0 for chunk_data in iterable: # _logger.debug('reading %d bytes' % size) @@ -93,11 +96,11 @@ def _read_multi_part_response(iterable, boundary): while part.find(end_of_line, 0, 2) == 0: part = part[2:] while part.rfind(end_of_line, len(part) - 2) == (len(part) - 2): - part = part[0:len(part) - 2] + part = part[0 : len(part) - 2] yield part - work = work[idx + len(boundary_header):] - if work[0] == '-' and work[1] == '-': - _logger.debug('end boundary reached') + work = work[idx + len(boundary_header) :] + if work[0] == "-" and work[1] == "-": + _logger.debug("end boundary reached") return else: idx = work.find(boundary_header) diff --git a/main/cloudfoundry_client/doppler/websocket_envelope_reader.py b/main/cloudfoundry_client/doppler/websocket_envelope_reader.py index 7be5743..46c279a 100644 --- a/main/cloudfoundry_client/doppler/websocket_envelope_reader.py +++ b/main/cloudfoundry_client/doppler/websocket_envelope_reader.py @@ -5,8 +5,14 @@ class WebsocketFrameReader(object): - def __init__(self, url, access_token_provider: Callable[[], str], verify_ssl: bool = True, - proxy_host: Optional[str] = None, proxy_port: Optional[int] = None): + def __init__( + self, + url, + access_token_provider: Callable[[], str], + verify_ssl: bool = True, + proxy_host: Optional[str] = None, + proxy_port: Optional[int] = None, + ): if not verify_ssl: self._ws = websocket.WebSocket(sslopt=dict(cert_reqs=ssl.CERT_NONE)) else: @@ -17,10 +23,10 @@ def __init__(self, url, access_token_provider: Callable[[], str], verify_ssl: bo self._access_token_provider = access_token_provider def connect(self): - kw_args = dict(header=dict(Authorization='Bearer %s' % self._access_token_provider())) + kw_args = dict(header=dict(Authorization="Bearer %s" % self._access_token_provider())) if self._proxy_host is not None and self._proxy_port is not None: - kw_args['http_proxy_host'] = self._proxy_host - kw_args['http_proxy_port'] = str(self._proxy_port) + kw_args["http_proxy_host"] = self._proxy_host + kw_args["http_proxy_port"] = str(self._proxy_port) self._ws.connect(self._url, **kw_args) def close(self): @@ -38,5 +44,5 @@ def __iter__(self): try: for frame in self._ws: yield frame - except websocket.WebSocketConnectionClosedException as _: + except websocket.WebSocketConnectionClosedException: pass diff --git a/main/cloudfoundry_client/errors.py b/main/cloudfoundry_client/errors.py index d4b3d16..ef71f3f 100644 --- a/main/cloudfoundry_client/errors.py +++ b/main/cloudfoundry_client/errors.py @@ -15,9 +15,9 @@ def __str__(self): if self.body is None: return self.status_code.name elif type(self.body) == str: - return '%s : %s' % (self.status_code.name, self.body) + return "%s : %s" % (self.status_code.name, self.body) else: - return '%s : %s' % (self.status_code.name, json.dumps(self.body)) + return "%s : %s" % (self.status_code.name, json.dumps(self.body)) class InvalidEntity(Exception): @@ -26,4 +26,4 @@ def __init__(self, **kwargs): self.raw_entity = dict(**kwargs) def __str__(self): - return 'InvalidEntity: %s' % json.dumps(self.raw_entity) + return "InvalidEntity: %s" % json.dumps(self.raw_entity) diff --git a/main/cloudfoundry_client/json_object.py b/main/cloudfoundry_client/json_object.py index 0de6b56..590b9f3 100644 --- a/main/cloudfoundry_client/json_object.py +++ b/main/cloudfoundry_client/json_object.py @@ -5,4 +5,4 @@ class JsonObject(dict): def __init__(self, *args, **kwargs): super(JsonObject, self).__init__(*args, **kwargs) - json = json.dumps \ No newline at end of file + json = json.dumps diff --git a/main/cloudfoundry_client/main/apps_command_domain.py b/main/cloudfoundry_client/main/apps_command_domain.py index 1be117c..3d946b8 100644 --- a/main/cloudfoundry_client/main/apps_command_domain.py +++ b/main/cloudfoundry_client/main/apps_command_domain.py @@ -1,4 +1,4 @@ -from argparse import Action, _SubParsersAction, Namespace +from argparse import _SubParsersAction, Namespace from typing import Callable from cloudfoundry_client.client import CloudFoundryClient @@ -7,31 +7,50 @@ class AppCommandDomain(CommandDomain): def __init__(self): - super(AppCommandDomain, self).__init__(display_name='Applications', - entity_name='app', - filter_list_parameters=['organization_guid', 'space_guid'], - allow_retrieve_by_name=True, - allow_deletion=True, - extra_methods=[(self.recent_logs(), 'Recent Logs',), - (self.stream_logs(), 'Stream Logs',), - (self.simple_extra_command('env'), - 'Get the environment of an application'), - (self.simple_extra_command('instances'), - 'Get the instances of an application',), - (self.simple_extra_command('stats'), - 'Get the stats of an application',), - (self.simple_extra_command('summary'), - 'Get the summary of an application',), - (self.simple_extra_command('start'), - 'Start an application',), - (self.simple_extra_command('stop'), - 'Stop an application',), - (self.simple_extra_command('restage'), - 'Restage an application',), - (self.app_routes(), - 'List the routes(host) of an application'), - (self.restart_instance(), - 'Restart the instance of an application')]) + super(AppCommandDomain, self).__init__( + display_name="Applications", + entity_name="app", + filter_list_parameters=["organization_guid", "space_guid"], + allow_retrieve_by_name=True, + allow_deletion=True, + extra_methods=[ + ( + self.recent_logs(), + "Recent Logs", + ), + ( + self.stream_logs(), + "Stream Logs", + ), + (self.simple_extra_command("env"), "Get the environment of an application"), + ( + self.simple_extra_command("instances"), + "Get the instances of an application", + ), + ( + self.simple_extra_command("stats"), + "Get the stats of an application", + ), + ( + self.simple_extra_command("summary"), + "Get the summary of an application", + ), + ( + self.simple_extra_command("start"), + "Start an application", + ), + ( + self.simple_extra_command("stop"), + "Stop an application", + ), + ( + self.simple_extra_command("restage"), + "Restage an application", + ), + (self.app_routes(), "List the routes(host) of an application"), + (self.restart_instance(), "Restart the instance of an application"), + ], + ) def recent_logs(self) -> Command: def execute(client, arguments): @@ -39,7 +58,7 @@ def execute(client, arguments): for envelope in client.doppler.recent_logs(resource_id): print(envelope) - return Command('recent_logs', self._generate_id_command_parser('recent_logs'), execute) + return Command("recent_logs", self._generate_id_command_parser("recent_logs"), execute) def stream_logs(self) -> Command: def execute(client, arguments): @@ -50,7 +69,7 @@ def execute(client, arguments): except KeyboardInterrupt: pass - return Command('stream_logs', self._generate_id_command_parser('stream_logs'), execute) + return Command("stream_logs", self._generate_id_command_parser("stream_logs"), execute) def simple_extra_command(self, entry) -> Command: def execute(client, arguments): @@ -62,31 +81,32 @@ def execute(client, arguments): def app_routes(self) -> Command: def execute(client: CloudFoundryClient, arguments: Namespace): resource_id = self.resolve_id(arguments.id[0], lambda x: self._get_client_domain(client).get_first(name=x)) - for entity in getattr(self._get_client_domain(client), 'list_routes')(resource_id): - print('%s - %s' % (entity['metadata']['guid'], entity['entity']['host'])) + for entity in getattr(self._get_client_domain(client), "list_routes")(resource_id): + print("%s - %s" % (entity["metadata"]["guid"], entity["entity"]["host"])) - return Command('app_routes', self._generate_id_command_parser('app_routes'), execute) + return Command("app_routes", self._generate_id_command_parser("app_routes"), execute) def restart_instance(self) -> Command: def generate_parser(parser: _SubParsersAction): - command_parser = parser.add_parser('restart_instance') - command_parser.add_argument('id', metavar='ids', type=str, nargs=1, - help='The id. Can be UUID or name (first found then)') - command_parser.add_argument('instance_id', metavar='instance_ids', type=int, nargs=1, - help='The instance id') + command_parser = parser.add_parser("restart_instance") + command_parser.add_argument( + "id", metavar="ids", type=str, nargs=1, help="The id. Can be UUID or name (first found then)" + ) + command_parser.add_argument("instance_id", metavar="instance_ids", type=int, nargs=1, help="The instance id") def execute(client: CloudFoundryClient, arguments: Namespace): app_domain = self._get_client_domain(client) resource_id = self.resolve_id(arguments.id[0], lambda x: app_domain.get_first(name=x)) - getattr(app_domain, 'restart_instance')(resource_id, int(arguments.instance_id[0])) + getattr(app_domain, "restart_instance")(resource_id, int(arguments.instance_id[0])) - return Command('restart_instance', generate_parser, execute) + return Command("restart_instance", generate_parser, execute) @staticmethod def _generate_id_command_parser(entry: str) -> Callable[[_SubParsersAction], None]: def generate_parser(parser: _SubParsersAction): command_parser = parser.add_parser(entry) - command_parser.add_argument('id', metavar='ids', type=str, nargs=1, - help='The id. Can be UUID or name (first found then)') + command_parser.add_argument( + "id", metavar="ids", type=str, nargs=1, help="The id. Can be UUID or name (first found then)" + ) return generate_parser diff --git a/main/cloudfoundry_client/main/command_domain.py b/main/cloudfoundry_client/main/command_domain.py index 4d8f863..5a80e3b 100644 --- a/main/cloudfoundry_client/main/command_domain.py +++ b/main/cloudfoundry_client/main/command_domain.py @@ -13,19 +13,30 @@ class Command(object): - def __init__(self, entry: str, - generate_parser: Callable[[_SubParsersAction], Any], - execute: Callable[[CloudFoundryClient, Namespace], Any]): + def __init__( + self, + entry: str, + generate_parser: Callable[[_SubParsersAction], Any], + execute: Callable[[CloudFoundryClient, Namespace], Any], + ): self.entry = entry self.generate_parser = generate_parser self.execute = execute class CommandDomain(object): - def __init__(self, display_name: str, entity_name: str, filter_list_parameters: list, - api_version: str = 'v2', name_property: str = 'name', - allow_retrieve_by_name: bool = False, allow_creation: bool = False, allow_deletion: bool = False, - extra_methods: list = None): + def __init__( + self, + display_name: str, + entity_name: str, + filter_list_parameters: list, + api_version: str = "v2", + name_property: str = "name", + allow_retrieve_by_name: bool = False, + allow_creation: bool = False, + allow_deletion: bool = False, + extra_methods: list = None, + ): self.display_name = display_name self.client_domain = self.plural(entity_name) self.api_version = api_version @@ -50,18 +61,18 @@ def __init__(self, display_name: str, entity_name: str, filter_list_parameters: self.extra_description[command[0].entry] = command[1] def description(self) -> List[str]: - description = [' %s' % self.display_name, - ' %s : List %ss' % (self._list_entry(), self.entity_name), - ' %s : Get a %s by %s' % (self._get_entry(), self.entity_name, - 'UUID or name (first found then)' - if self.allow_retrieve_by_name - else 'UUID')] + description = [ + " %s" % self.display_name, + " %s : List %ss" % (self._list_entry(), self.entity_name), + " %s : Get a %s by %s" + % (self._get_entry(), self.entity_name, "UUID or name (first found then)" if self.allow_retrieve_by_name else "UUID"), + ] if self.allow_creation: - description.append(' %s : Create a %s' % (self._create_entry(), self.entity_name)) + description.append(" %s : Create a %s" % (self._create_entry(), self.entity_name)) if self.allow_deletion: - description.append(' %s : Delete a %s' % (self._delete_entry(), self.entity_name)) - description.extend([' %s : %s' % (k, v) for k, v in self.extra_description.items()]) + description.append(" %s : Delete a %s" % (self._delete_entry(), self.entity_name)) + description.extend([" %s : %s" % (k, v) for k, v in self.extra_description.items()]) return description def generate_parser(self, parser: _SubParsersAction): @@ -79,20 +90,20 @@ def _get_client_domain(self, client: CloudFoundryClient) -> Any: @staticmethod def plural(entity_name: str) -> str: - if entity_name.endswith('y') and not (re.match(r'.+[aeiou]y', entity_name)): - return '%sies' % entity_name[:len(entity_name) - 1] + if entity_name.endswith("y") and not (re.match(r".+[aeiou]y", entity_name)): + return "%sies" % entity_name[: len(entity_name) - 1] else: - return '%ss' % entity_name + return "%ss" % entity_name @staticmethod def is_guid(s: str) -> bool: - return re.match(r'[\d|a-z]{8}-[\d|a-z]{4}-[\d|a-z]{4}-[\d|a-z]{4}-[\d|a-z]{12}', s.lower()) is not None + return re.match(r"[\d|a-z]{8}-[\d|a-z]{4}-[\d|a-z]{4}-[\d|a-z]{4}-[\d|a-z]{12}", s.lower()) is not None def id(self, entity: JsonObject) -> str: - if self.api_version == 'v2': - return entity['metadata']['guid'] - elif self.api_version == 'v3': - return entity['guid'] + if self.api_version == "v2": + return entity["metadata"]["guid"] + elif self.api_version == "v3": + return entity["guid"] def resolve_id(self, argument: str, get_by_name: Callable[[str], JsonObject]) -> str: if CommandDomain.is_guid(argument): @@ -100,19 +111,19 @@ def resolve_id(self, argument: str, get_by_name: Callable[[str], JsonObject]) -> elif self.allow_retrieve_by_name: result = get_by_name(argument) if result is not None: - if self.api_version == 'v2': - return result['metadata']['guid'] - elif self.api_version == 'v3': - return result['guid'] + if self.api_version == "v2": + return result["metadata"]["guid"] + elif self.api_version == "v3": + return result["guid"] else: - raise InvalidStatusCode(HTTPStatus.NOT_FOUND, '%s with name %s' % (self.client_domain, argument)) + raise InvalidStatusCode(HTTPStatus.NOT_FOUND, "%s with name %s" % (self.client_domain, argument)) else: - raise ValueError('id: %s: does not allow search by name' % self.client_domain) + raise ValueError("id: %s: does not allow search by name" % self.client_domain) def name(self, entity: JsonObject) -> str: - if self.api_version == 'v2': - return entity['entity'][self.name_property] - elif self.api_version == 'v3': + if self.api_version == "v2": + return entity["entity"][self.name_property] + elif self.api_version == "v3": return entity[self.name_property] def find_by_name(self, client: CloudFoundryClient, name: str) -> JsonObject: @@ -124,23 +135,28 @@ def create(self) -> Command: def execute(client: CloudFoundryClient, arguments: Namespace): data = None if os.path.isfile(arguments.entity[0]): - with open(arguments.entity[0], 'r') as f: + with open(arguments.entity[0], "r") as f: try: data = json.load(f) except ValueError: - raise ValueError('entity: file %s does not contain valid json data' % arguments.entity[0]) + raise ValueError("entity: file %s does not contain valid json data" % arguments.entity[0]) else: try: data = json.loads(arguments.entity[0]) except ValueError: - raise ValueError('entity: must be either a valid json file path or a json object') + raise ValueError("entity: must be either a valid json file path or a json object") print(self._get_client_domain(client)._create(data).json()) def generate_parser(parser: _SubParsersAction): create_parser = parser.add_parser(entry) - create_parser.add_argument('entity', metavar='entities', type=str, nargs=1, - help='Either a path of the json file containing the %s or a json object or the json %s object' % ( - self.client_domain, self.client_domain)) + create_parser.add_argument( + "entity", + metavar="entities", + type=str, + nargs=1, + help="Either a path of the json file containing the %s or a json object or the json %s object" + % (self.client_domain, self.client_domain), + ) return Command(entry, generate_parser, execute) @@ -153,18 +169,21 @@ def execute(client: CloudFoundryClient, arguments: Namespace): elif self.allow_retrieve_by_name: entity = self.find_by_name(client, arguments.id[0]) if entity is None: - raise InvalidStatusCode(HTTPStatus.NOT_FOUND, - '%s with name %s' % (self.client_domain, arguments.id[0])) + raise InvalidStatusCode(HTTPStatus.NOT_FOUND, "%s with name %s" % (self.client_domain, arguments.id[0])) else: self._get_client_domain(client)._remove(self.id(entity)) else: - raise ValueError('id: %s: does not allow search by name' % self.client_domain) + raise ValueError("id: %s: does not allow search by name" % self.client_domain) def generate_parser(parser: _SubParsersAction): delete_parser = parser.add_parser(entry) - delete_parser.add_argument('id', metavar='ids', type=str, nargs=1, - help='The id. Can be UUID or name (first found then)' - if self.allow_retrieve_by_name else 'The id (UUID)') + delete_parser.add_argument( + "id", + metavar="ids", + type=str, + nargs=1, + help="The id. Can be UUID or name (first found then)" if self.allow_retrieve_by_name else "The id (UUID)", + ) return Command(entry, generate_parser, execute) @@ -172,15 +191,18 @@ def get(self) -> Command: entry = self._get_entry() def execute(client: CloudFoundryClient, arguments: Namespace): - resource_id = self.resolve_id(arguments.id[0], - functools.partial(self.find_by_name, client)) + resource_id = self.resolve_id(arguments.id[0], functools.partial(self.find_by_name, client)) print(self._get_client_domain(client).get(resource_id).json(indent=1)) def generate_parser(parser: _SubParsersAction): get_parser = parser.add_parser(entry) - get_parser.add_argument('id', metavar='ids', type=str, nargs=1, - help='The id. Can be UUID or name (first found then)' - if self.allow_retrieve_by_name else 'The id (UUID)') + get_parser.add_argument( + "id", + metavar="ids", + type=str, + nargs=1, + help="The id. Can be UUID or name (first found then)" if self.allow_retrieve_by_name else "The id (UUID)", + ) return Command(entry, generate_parser, execute) @@ -195,26 +217,32 @@ def execute(client: CloudFoundryClient, arguments: Namespace): filter_list[filter_parameter] = filter_value for entity in self._get_client_domain(client).list(**filter_list): if self.name_property is not None: - print('%s - %s' % (self.id(entity), self.name(entity))) + print("%s - %s" % (self.id(entity), self.name(entity))) else: print(self.id(entity)) def generate_parser(parser: _SubParsersAction): list_parser = parser.add_parser(entry) for filter_parameter in self.filter_list_parameters: - list_parser.add_argument('-%s' % filter_parameter, action='store', dest=filter_parameter, type=str, - default=None, help='Filter with %s' % filter_parameter) + list_parser.add_argument( + "-%s" % filter_parameter, + action="store", + dest=filter_parameter, + type=str, + default=None, + help="Filter with %s" % filter_parameter, + ) return Command(entry, generate_parser, execute) def _list_entry(self) -> str: - return 'list_%s' % self.plural(self.entity_name) + return "list_%s" % self.plural(self.entity_name) def _create_entry(self) -> str: - return 'create_%s' % self.entity_name + return "create_%s" % self.entity_name def _delete_entry(self) -> str: - return 'delete_%s' % self.entity_name + return "delete_%s" % self.entity_name def _get_entry(self) -> str: - return 'get_%s' % self.entity_name + return "get_%s" % self.entity_name diff --git a/main/cloudfoundry_client/main/main.py b/main/cloudfoundry_client/main/main.py index bd9a016..0fc2045 100644 --- a/main/cloudfoundry_client/main/main.py +++ b/main/cloudfoundry_client/main/main.py @@ -19,90 +19,95 @@ from cloudfoundry_client.main.operation_commands import generate_push_command from cloudfoundry_client.main.tasks_command_domain import TaskCommandDomain -__all__ = ['main', 'build_client_from_configuration'] +__all__ = ["main", "build_client_from_configuration"] _logger = logging.getLogger(__name__) -def _read_value_from_user(prompt: str, error_message: str = None, validator: Callable[[str], bool] = None, - default: str = '') -> str: +def _read_value_from_user( + prompt: str, error_message: str = None, validator: Callable[[str], bool] = None, default: str = "" +) -> str: while True: - sys.stdout.write('%s [%s]: ' % (prompt, default)) + sys.stdout.write("%s [%s]: " % (prompt, default)) sys.stdout.flush() - answer_value = sys.stdin.readline().rstrip(' \r\n') + answer_value = sys.stdin.readline().rstrip(" \r\n") if len(answer_value) == 0: answer_value = default if len(answer_value) > 0 and (validator is None or validator(answer_value)): return answer_value else: if error_message is None: - sys.stderr.write('\"%s\": invalid value\n' % answer_value) + sys.stderr.write('"%s": invalid value\n' % answer_value) else: - sys.stderr.write('\"%s\": %s\n' % (answer_value, error_message)) + sys.stderr.write('"%s": %s\n' % (answer_value, error_message)) def get_user_directory() -> str: - dir_conf = os.path.join(os.path.expanduser('~')) + dir_conf = os.path.join(os.path.expanduser("~")) if not os.path.isdir(dir_conf): if os.path.exists(dir_conf): - raise IOError('%s exists but is not a directory') + raise IOError("%s exists but is not a directory") os.mkdir(dir_conf) return dir_conf def get_config_file() -> str: - return os.path.join(get_user_directory(), '.cf_client_python.json') + return os.path.join(get_user_directory(), ".cf_client_python.json") def import_from_clf_cli(): user_directory = get_user_directory() - cf_cli_dir = os.path.join(user_directory, '.cf') + cf_cli_dir = os.path.join(user_directory, ".cf") if not os.path.isdir(cf_cli_dir): - raise IOError('%s directory not found' % cf_cli_dir) - config_file = os.path.join(cf_cli_dir, 'config.json') + raise IOError("%s directory not found" % cf_cli_dir) + config_file = os.path.join(cf_cli_dir, "config.json") if not os.path.isfile(config_file): - raise IOError('%s not found' % config_file) - with open(config_file, 'r') as cf_cli_file: + raise IOError("%s not found" % config_file) + with open(config_file, "r") as cf_cli_file: cf_cli_data = json.load(cf_cli_file) - if cf_cli_data['RefreshToken'] is None or cf_cli_data['Target'] is None: - raise IOError('Could not load informations from cf cli configuration') - with open(get_config_file(), 'w') as f: - f.write(json.dumps(dict(target_endpoint=cf_cli_data['Target'], - verify=False, - refresh_token=cf_cli_data['RefreshToken']), indent=2)) + if cf_cli_data["RefreshToken"] is None or cf_cli_data["Target"] is None: + raise IOError("Could not load informations from cf cli configuration") + with open(get_config_file(), "w") as f: + f.write( + json.dumps( + dict(target_endpoint=cf_cli_data["Target"], verify=False, refresh_token=cf_cli_data["RefreshToken"]), indent=2 + ) + ) def build_client_from_configuration(previous_configuration: dict = None) -> CloudFoundryClient: config_file = get_config_file() if not os.path.isfile(config_file): - target_endpoint = _read_value_from_user('Please enter a target endpoint', - 'Url must starts with http:// or https://', - lambda s: s.startswith('http://') or s.startswith('https://'), - default='' if previous_configuration is None else - previous_configuration.get('target_endpoint', '')) - verify = _read_value_from_user('Verify ssl (true/false)', - 'Enter either true or false', - lambda s: s == 'true' or s == 'false', - default='true' if previous_configuration is None else - json.dumps( - previous_configuration.get('verify', True))) - login = _read_value_from_user('Please enter your login') - password = _read_value_from_user('Please enter your password') - client = CloudFoundryClient(target_endpoint, verify=(verify == 'true')) + target_endpoint = _read_value_from_user( + "Please enter a target endpoint", + "Url must starts with http:// or https://", + lambda s: s.startswith("http://") or s.startswith("https://"), + default="" if previous_configuration is None else previous_configuration.get("target_endpoint", ""), + ) + verify = _read_value_from_user( + "Verify ssl (true/false)", + "Enter either true or false", + lambda s: s == "true" or s == "false", + default="true" if previous_configuration is None else json.dumps(previous_configuration.get("verify", True)), + ) + login = _read_value_from_user("Please enter your login") + password = _read_value_from_user("Please enter your password") + client = CloudFoundryClient(target_endpoint, verify=(verify == "true")) client.init_with_user_credentials(login, password) - with open(config_file, 'w') as f: - f.write(json.dumps(dict(target_endpoint=target_endpoint, - verify=(verify == 'true'), - refresh_token=client.refresh_token), indent=2)) + with open(config_file, "w") as f: + f.write( + json.dumps( + dict(target_endpoint=target_endpoint, verify=(verify == "true"), refresh_token=client.refresh_token), indent=2 + ) + ) return client else: try: configuration = None - with open(config_file, 'r') as f: + with open(config_file, "r") as f: configuration = json.load(f) - client = CloudFoundryClient(configuration['target_endpoint'], - verify=configuration['verify']) - client.init_with_token(configuration['refresh_token']) + client = CloudFoundryClient(configuration["target_endpoint"], verify=configuration["verify"]) + client.init_with_token(configuration["refresh_token"]) return client except Exception as ex: if type(ex) == ConnectionError: @@ -114,21 +119,20 @@ def build_client_from_configuration(previous_configuration: dict = None) -> Clou def is_guid(s: str) -> bool: - return re.match(r'[\d|a-z]{8}-[\d|a-z]{4}-[\d|a-z]{4}-[\d|a-z]{4}-[\d|a-z]{12}', s.lower()) is not None + return re.match(r"[\d|a-z]{8}-[\d|a-z]{4}-[\d|a-z]{4}-[\d|a-z]{4}-[\d|a-z]{12}", s.lower()) is not None -def resolve_id(argument: str, get_by_name: Callable[[str], JsonObject], domain_name: str, - allow_search_by_name: bool) -> str: +def resolve_id(argument: str, get_by_name: Callable[[str], JsonObject], domain_name: str, allow_search_by_name: bool) -> str: if is_guid(argument): return argument elif allow_search_by_name: result = get_by_name(argument) if result is not None: - return result['metadata']['guid'] + return result["metadata"]["guid"] else: - raise InvalidStatusCode(HTTPStatus.NOT_FOUND, '%s with name %s' % (domain_name, argument)) + raise InvalidStatusCode(HTTPStatus.NOT_FOUND, "%s with name %s" % (domain_name, argument)) else: - raise ValueError('id: %s: does not allow search by name' % domain_name) + raise ValueError("id: %s: does not allow search by name" % domain_name) def log_recent(client: CloudFoundryClient, application_guid: str): @@ -145,65 +149,119 @@ def stream_logs(client: CloudFoundryClient, application_guid: str): def _get_v2_client_domain(client: CloudFoundryClient, domain: str) -> Any: - return getattr(client.v2, '%ss' % domain) + return getattr(client.v2, "%ss" % domain) def generate_oauth_token_command() -> Tuple[Command, str]: - entry = 'oauth-token' + entry = "oauth-token" def generate_parser(parser: argparse._SubParsersAction): parser.add_parser(entry) def execute(client: CloudFoundryClient, arguments: argparse.Namespace): token = client._access_token - print(token if token is not None else 'No token') + print(token if token is not None else "No token") - return Command(entry, generate_parser, execute), 'Display oauth token' + return Command(entry, generate_parser, execute), "Display oauth token" def main(): - logging.basicConfig(level=logging.INFO, - format='%(message)s') + logging.basicConfig(level=logging.INFO, format="%(message)s") logging.getLogger("requests").setLevel(logging.WARNING) logging.getLogger("urllib3").setLevel(logging.WARNING) commands = [ - CommandDomain(display_name='Organizations', entity_name='organization', - api_version='v3', - filter_list_parameters=['names', 'guids'], allow_retrieve_by_name=True, - allow_creation=True, allow_deletion=True), - CommandDomain(display_name='Spaces', entity_name='space', filter_list_parameters=['organization_guid'], - allow_retrieve_by_name=True, allow_creation=True, allow_deletion=True), + CommandDomain( + display_name="Organizations", + entity_name="organization", + api_version="v3", + filter_list_parameters=["names", "guids"], + allow_retrieve_by_name=True, + allow_creation=True, + allow_deletion=True, + ), + CommandDomain( + display_name="Spaces", + entity_name="space", + filter_list_parameters=["organization_guid"], + allow_retrieve_by_name=True, + allow_creation=True, + allow_deletion=True, + ), AppCommandDomain(), - CommandDomain(display_name='Services', entity_name='service', filter_list_parameters=['service_broker_guid'], - name_property='label', allow_retrieve_by_name=True, allow_creation=True, allow_deletion=True), - CommandDomain(display_name='Service Plans', entity_name='service_plan', - filter_list_parameters=['service_guid', 'service_instance_guid', 'service_broker_guid']), - CommandDomain(display_name='Service Instances', entity_name='service_instance', - filter_list_parameters=['organization_guid', 'space_guid', 'service_plan_guid'], - allow_creation=True, allow_deletion=True), - CommandDomain(display_name='Service Keys', entity_name='service_key', - filter_list_parameters=['service_instance_guid'], - allow_creation=True, allow_deletion=True), - CommandDomain(display_name='Service Bindings', entity_name='service_binding', - filter_list_parameters=['app_guid', 'service_instance_guid'], name_property=None, - allow_creation=True, allow_deletion=True), - CommandDomain(display_name='Service Broker', entity_name='service_broker', - filter_list_parameters=['name', 'space_guid'], - allow_retrieve_by_name=True, allow_creation=True, allow_deletion=True), - CommandDomain(display_name='Service Plan Visibilities', entity_name='service_plan_visibility', - filter_list_parameters=['organization_guid', 'service_plan_guid'], name_property=None, - allow_retrieve_by_name=False, allow_creation=True, allow_deletion=True), - CommandDomain(display_name='Buildpacks', entity_name='buildpack', - api_version='v3', - filter_list_parameters=['names', 'stacks'], allow_retrieve_by_name=True, - allow_creation=True, allow_deletion=True), - CommandDomain(display_name='Domains', entity_name='domain', - api_version='v3', - filter_list_parameters=[], allow_retrieve_by_name=True, - allow_creation=True, allow_deletion=True), - CommandDomain(display_name='Routes', entity_name='route', name_property='host', filter_list_parameters=[]), - TaskCommandDomain() + CommandDomain( + display_name="Services", + entity_name="service", + filter_list_parameters=["service_broker_guid"], + name_property="label", + allow_retrieve_by_name=True, + allow_creation=True, + allow_deletion=True, + ), + CommandDomain( + display_name="Service Plans", + entity_name="service_plan", + filter_list_parameters=["service_guid", "service_instance_guid", "service_broker_guid"], + ), + CommandDomain( + display_name="Service Instances", + entity_name="service_instance", + filter_list_parameters=["organization_guid", "space_guid", "service_plan_guid"], + allow_creation=True, + allow_deletion=True, + ), + CommandDomain( + display_name="Service Keys", + entity_name="service_key", + filter_list_parameters=["service_instance_guid"], + allow_creation=True, + allow_deletion=True, + ), + CommandDomain( + display_name="Service Bindings", + entity_name="service_binding", + filter_list_parameters=["app_guid", "service_instance_guid"], + name_property=None, + allow_creation=True, + allow_deletion=True, + ), + CommandDomain( + display_name="Service Broker", + entity_name="service_broker", + filter_list_parameters=["name", "space_guid"], + allow_retrieve_by_name=True, + allow_creation=True, + allow_deletion=True, + ), + CommandDomain( + display_name="Service Plan Visibilities", + entity_name="service_plan_visibility", + filter_list_parameters=["organization_guid", "service_plan_guid"], + name_property=None, + allow_retrieve_by_name=False, + allow_creation=True, + allow_deletion=True, + ), + CommandDomain( + display_name="Buildpacks", + entity_name="buildpack", + api_version="v3", + filter_list_parameters=["names", "stacks"], + allow_retrieve_by_name=True, + allow_creation=True, + allow_deletion=True, + ), + CommandDomain( + display_name="Domains", + entity_name="domain", + api_version="v3", + filter_list_parameters=[], + allow_retrieve_by_name=True, + allow_creation=True, + allow_deletion=True, + ), + CommandDomain(display_name="Routes", entity_name="route", name_property="host", filter_list_parameters=[]), + TaskCommandDomain(), ] operation_commands = [generate_push_command()] others_commands = [generate_oauth_token_command()] @@ -212,18 +270,18 @@ def main(): for command in commands: descriptions.extend(command.description()) - descriptions.append('Operations') + descriptions.append("Operations") for command, description in operation_commands: - descriptions.append(' %s: %s' % (command.entry, description)) + descriptions.append(" %s: %s" % (command.entry, description)) - descriptions.append('Others') + descriptions.append("Others") for command, description in others_commands: - descriptions.append(' %s: %s' % (command.entry, description)) + descriptions.append(" %s: %s" % (command.entry, description)) parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter) - parser.add_argument('-V', '--version', action='version', version=__version__) - subparsers = parser.add_subparsers(title='Commands', dest='action', description='\n'.join(descriptions)) - subparsers.add_parser('import_from_cf_cli', help='Copy CF CLI configuration into our configuration') + parser.add_argument("-V", "--version", action="version", version=__version__) + subparsers = parser.add_subparsers(title="Commands", dest="action", description="\n".join(descriptions)) + subparsers.add_parser("import_from_cf_cli", help="Copy CF CLI configuration into our configuration") for command in commands: command.generate_parser(subparsers) @@ -232,7 +290,7 @@ def main(): command.generate_parser(subparsers) arguments = parser.parse_args() - if arguments.action == 'import_from_cf_cli': + if arguments.action == "import_from_cf_cli": import_from_clf_cli() else: client = build_client_from_configuration() diff --git a/main/cloudfoundry_client/main/operation_commands.py b/main/cloudfoundry_client/main/operation_commands.py index b661fb7..8ef39e1 100644 --- a/main/cloudfoundry_client/main/operation_commands.py +++ b/main/cloudfoundry_client/main/operation_commands.py @@ -7,17 +7,15 @@ def generate_push_command() -> Tuple[Command, str]: - entry = 'push_app' + entry = "push_app" def generate_parser(parser: _SubParsersAction): command_parser = parser.add_parser(entry) - command_parser.add_argument('manifest_path', metavar='manifest_paths', type=str, nargs=1, - help='The manifest path') - command_parser.add_argument('-space_guid', action='store', dest='space_guid', type=str, - help='Space guid') + command_parser.add_argument("manifest_path", metavar="manifest_paths", type=str, nargs=1, help="The manifest path") + command_parser.add_argument("-space_guid", action="store", dest="space_guid", type=str, help="Space guid") def execute(client: CloudFoundryClient, arguments: Namespace): manifest_path = arguments.manifest_path[0] PushOperation(client).push(arguments.space_guid, manifest_path) - return Command(entry, generate_parser, execute), 'Push an application by its manifest' + return Command(entry, generate_parser, execute), "Push an application by its manifest" diff --git a/main/cloudfoundry_client/main/tasks_command_domain.py b/main/cloudfoundry_client/main/tasks_command_domain.py index d50fd8a..dc76a9d 100644 --- a/main/cloudfoundry_client/main/tasks_command_domain.py +++ b/main/cloudfoundry_client/main/tasks_command_domain.py @@ -9,20 +9,29 @@ class TaskCommandDomain(CommandDomain): def __init__(self): - super(TaskCommandDomain, self).__init__(display_name='Tasks', entity_name='task', - filter_list_parameters=['names', 'app_guids', 'space_guids', - 'organization_guids'], - api_version='v3', allow_creation=True, allow_deletion=False, - extra_methods=[(self.cancel(), 'Cancel Task',)]) + super(TaskCommandDomain, self).__init__( + display_name="Tasks", + entity_name="task", + filter_list_parameters=["names", "app_guids", "space_guids", "organization_guids"], + api_version="v3", + allow_creation=True, + allow_deletion=False, + extra_methods=[ + ( + self.cancel(), + "Cancel Task", + ) + ], + ) def id(self, entity: JsonObject) -> str: - return entity['guid'] + return entity["guid"] def name(self, entity: JsonObject) -> str: return entity[self.name_property] def find_by_name(self, client: CloudFoundryClient, name: str): - return self._get_client_domain(client).get_first(**{'%ss' % self.name_property: name}) + return self._get_client_domain(client).get_first(**{"%ss" % self.name_property: name}) def create(self) -> Command: entry = self._create_entry() @@ -30,37 +39,40 @@ def create(self) -> Command: def execute(client: CloudFoundryClient, arguments: Namespace): data = None if os.path.isfile(arguments.entity[0]): - with open(arguments.entity[0], 'r') as f: + with open(arguments.entity[0], "r") as f: try: data = json.load(f) except ValueError: - raise ValueError('entity: file %s does not contain valid json data' % arguments.entity[0]) + raise ValueError("entity: file %s does not contain valid json data" % arguments.entity[0]) else: try: data = json.loads(arguments.entity[0]) except ValueError: - raise ValueError('entity: must be either a valid json file path or a json object') + raise ValueError("entity: must be either a valid json file path or a json object") print(self._get_client_domain(client).create(arguments.app_id[0], **data).json()) def generate_parser(parser: _SubParsersAction): create_parser = parser.add_parser(entry) - create_parser.add_argument('app_id', metavar='ids', type=str, nargs=1, - help='The application UUID.') - create_parser.add_argument('entity', metavar='entities', type=str, nargs=1, - help='Either a path of the json file containing the %s or a json object or the json %s object' % ( - self.client_domain, self.client_domain)) + create_parser.add_argument("app_id", metavar="ids", type=str, nargs=1, help="The application UUID.") + create_parser.add_argument( + "entity", + metavar="entities", + type=str, + nargs=1, + help="Either a path of the json file containing the %s or a json object or the json %s object" + % (self.client_domain, self.client_domain), + ) return Command(entry, generate_parser, execute) def cancel(self) -> Command: - entry = 'cancel_task' + entry = "cancel_task" def execute(client: CloudFoundryClient, arguments: Namespace): print(self._get_client_domain(client).cancel(arguments.id[0]).json(indent=1)) def generate_parser(parser: _SubParsersAction): command_parser = parser.add_parser(entry) - command_parser.add_argument('id', metavar='ids', type=str, nargs=1, - help='The task UUID') + command_parser.add_argument("id", metavar="ids", type=str, nargs=1, help="The task UUID") return Command(entry, generate_parser, execute) diff --git a/main/cloudfoundry_client/networking/entities.py b/main/cloudfoundry_client/networking/entities.py index 845756b..d68de5f 100644 --- a/main/cloudfoundry_client/networking/entities.py +++ b/main/cloudfoundry_client/networking/entities.py @@ -1,6 +1,6 @@ import logging -from functools import partial, reduce -from typing import Callable, List, Tuple, Any, Optional, Generator +from functools import reduce +from typing import Callable, List, Tuple, Any, Optional, Generator, TYPE_CHECKING from urllib.parse import quote from requests import Response @@ -9,22 +9,25 @@ from cloudfoundry_client.json_object import JsonObject from cloudfoundry_client.request_object import Request +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + _logger = logging.getLogger(__name__) class Entity(JsonObject): - def __init__(self, target_endpoint: str, client: 'CloudFoundryClient', *args, **kwargs): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient", *args, **kwargs): super(Entity, self).__init__(*args, **kwargs) self.target_endpoint = target_endpoint self.client = client try: - src = self['source'] - dst = self['destination'] - src['id'] - dst['id'] - dst['protocol'] - dst['ports']['start'] - dst['ports']['end'] + src = self["source"] + dst = self["destination"] + src["id"] + dst["id"] + dst["protocol"] + dst["ports"]["start"] + dst["ports"]["end"] except KeyError: raise InvalidEntity(**self) @@ -33,44 +36,47 @@ def __init__(self, target_endpoint: str, client: 'CloudFoundryClient', *args, ** class EntityManager(object): - list_query_parameters = ['page', 'results-per-page', 'order-direction'] + list_query_parameters = ["page", "results-per-page", "order-direction"] - list_multi_parameters = ['order-by'] + list_multi_parameters = ["order-by"] - def __init__(self, target_endpoint: str, client: 'CloudFoundryClient', entity_uri: str, - entity_builder: Optional[EntityBuilder] = None): + def __init__( + self, target_endpoint: str, client: "CloudFoundryClient", entity_uri: str, entity_builder: Optional[EntityBuilder] = None + ): self.target_endpoint = target_endpoint self.entity_uri = entity_uri self.client = client - self.entity_builder = entity_builder if entity_builder is not None else lambda pairs: Entity(target_endpoint, - client, pairs) - - def _list(self, requested_path: str, entity_builder: Optional[EntityBuilder] = None, **kwargs) \ - -> Generator[Entity, None, None]: - url_requested = self._get_url_filtered('%s%s' % (self.target_endpoint, requested_path), **kwargs) + self.entity_builder = ( + entity_builder if entity_builder is not None else lambda pairs: Entity(target_endpoint, client, pairs) + ) + + def _list( + self, requested_path: str, entity_builder: Optional[EntityBuilder] = None, **kwargs + ) -> Generator[Entity, None, None]: + url_requested = self._get_url_filtered("%s%s" % (self.target_endpoint, requested_path), **kwargs) response = self.client.get(url_requested) entity_builder = self._get_entity_builder(entity_builder) - _logger.debug('GET - %s - %s', url_requested, response.text) + _logger.debug("GET - %s - %s", url_requested, response.text) response_json = self._read_response(response, JsonObject) - for resource in response_json['policies']: + for resource in response_json["policies"]: yield entity_builder(list(resource.items())) def _create(self, data: dict, **kwargs) -> Entity: - url = '%s%s' % (self.target_endpoint, self.entity_uri) + url = "%s%s" % (self.target_endpoint, self.entity_uri) return self._post(url, data, **kwargs) def _remove(self, resource_id: str, **kwargs): - url = '%s%s/%s' % (self.target_endpoint, self.entity_uri, resource_id) + url = "%s%s/%s" % (self.target_endpoint, self.entity_uri, resource_id) self._delete(url, **kwargs) def _post(self, url: str, data: Optional[dict] = None, **kwargs): response = self.client.post(url, json=data, **kwargs) - _logger.debug('POST - %s - %s', url, response.text) + _logger.debug("POST - %s - %s", url, response.text) return self._read_response(response) def _delete(self, url: str, **kwargs): response = self.client.delete(url, **kwargs) - _logger.debug('DELETE - %s - %s', url, response.text) + _logger.debug("DELETE - %s - %s", url, response.text) def __iter__(self) -> Generator[Entity, None, None]: return self.list() @@ -79,7 +85,7 @@ def list(self, **kwargs) -> Generator[Entity, None, None]: return self._list(self.entity_uri, **kwargs) def get_first(self, **kwargs) -> Optional[Entity]: - kwargs.setdefault('results-per-page', 1) + kwargs.setdefault("results-per-page", 1) for entity in self._list(self.entity_uri, **kwargs): return entity return None @@ -100,25 +106,23 @@ def _get_entity_builder(self, entity_builder: Optional[EntityBuilder]) -> Entity return entity_builder def _get_url_filtered(self, url: str, **kwargs) -> str: - def _append_encoded_parameter(parameters: List[str], args: Tuple[str, Any]) -> List[str]: parameter_name, parameter_value = args[0], args[1] if parameter_name in self.list_query_parameters: - parameters.append('%s=%s' % (parameter_name, str(parameter_value))) + parameters.append("%s=%s" % (parameter_name, str(parameter_value))) elif parameter_name in self.list_multi_parameters: value_list = parameter_value if not isinstance(value_list, (list, tuple)): value_list = [value_list] for value in value_list: - parameters.append('%s=%s' % (parameter_name, str(value))) + parameters.append("%s=%s" % (parameter_name, str(value))) elif isinstance(parameter_value, (list, tuple)): - parameters.append('q=%s' % quote('%s IN %s' % (parameter_name, ','.join(parameter_value)))) + parameters.append("q=%s" % quote("%s IN %s" % (parameter_name, ",".join(parameter_value)))) else: - parameters.append('q=%s' % quote('%s:%s' % (parameter_name, str(parameter_value)))) + parameters.append("q=%s" % quote("%s:%s" % (parameter_name, str(parameter_value)))) return parameters if len(kwargs) > 0: - return '%s?%s' % (url, - "&".join(reduce(_append_encoded_parameter, sorted(list(kwargs.items())), []))) + return "%s?%s" % (url, "&".join(reduce(_append_encoded_parameter, sorted(list(kwargs.items())), []))) else: return url diff --git a/main/cloudfoundry_client/networking/v1/external/policies.py b/main/cloudfoundry_client/networking/v1/external/policies.py index 17bf611..3b7c094 100644 --- a/main/cloudfoundry_client/networking/v1/external/policies.py +++ b/main/cloudfoundry_client/networking/v1/external/policies.py @@ -7,34 +7,35 @@ class Policy: def __init__(self, src_id: str, dst_id: str, proto: str, start_port: int, end_port: int): - self.source = {'id': src_id} + self.source = {"id": src_id} - self.destination = {'id': dst_id, 'ports': {}} + self.destination = {"id": dst_id, "ports": {}} - __protos = ['tcp', 'udp'] + __protos = ["tcp", "udp"] if proto.lower() in __protos: - self.destination['protocol'] = proto.lower() + self.destination["protocol"] = proto.lower() else: - raise ValueError('unknown protocol {got}, known values are {known}' - ''.format(got=proto, known=__protos)) + raise ValueError("unknown protocol {got}, known values are {known}" "".format(got=proto, known=__protos)) if 1 <= start_port <= 65535: - self.destination['ports']['start'] = start_port + self.destination["ports"]["start"] = start_port else: - raise ValueError('start port is out of range') + raise ValueError("start port is out of range") if 1 <= end_port <= 65535: - self.destination['ports']['end'] = end_port + self.destination["ports"]["end"] = end_port else: - raise ValueError('end port is out of range') + raise ValueError("end port is out of range") @classmethod def from_dict(cls, policy: dict): - return cls(src_id=policy['source']['id'], - dst_id=policy['destination']['id'], - proto=policy['destination']['protocol'], - start_port=policy['destination']['ports']['start'], - end_port=policy['destination']['ports']['end']) + return cls( + src_id=policy["source"]["id"], + dst_id=policy["destination"]["id"], + proto=policy["destination"]["protocol"], + start_port=policy["destination"]["ports"]["start"], + end_port=policy["destination"]["ports"]["end"], + ) def dump(self): return self.__dict__ @@ -42,7 +43,7 @@ def dump(self): class PolicyManager(EntityManager): def __init__(self, target_endpoint, client): - super(PolicyManager, self).__init__(target_endpoint, client, '/networking/v1/external/policies') + super(PolicyManager, self).__init__(target_endpoint, client, "/networking/v1/external/policies") def create(self, policies: List[Policy]): """create a new network policy @@ -59,7 +60,7 @@ def create(self, policies: List[Policy]): if not isinstance(policy, Policy): raise TypeError data.append(policy.dump()) - return super(PolicyManager, self)._create({'policies': data}) + return super(PolicyManager, self)._create({"policies": data}) def delete(self, policies: List[Policy]): """remove a new network policy @@ -76,4 +77,4 @@ def delete(self, policies: List[Policy]): if not isinstance(policy, Policy): raise TypeError data.append(policy.dump()) - return super(PolicyManager, self)._delete({'policies': data}) + return super(PolicyManager, self)._delete({"policies": data}) diff --git a/main/cloudfoundry_client/operations/push/cf_ignore.py b/main/cloudfoundry_client/operations/push/cf_ignore.py index 6d2756e..e04033f 100644 --- a/main/cloudfoundry_client/operations/push/cf_ignore.py +++ b/main/cloudfoundry_client/operations/push/cf_ignore.py @@ -8,32 +8,33 @@ class CfIgnore(object): def __init__(self, application_path: str): - ignore_file_path = os.path.join(application_path, '.cfignore') + ignore_file_path = os.path.join(application_path, ".cfignore") self.ignore_items = [] if os.path.isfile(ignore_file_path): - with open(ignore_file_path, 'r') as ignore_file: + with open(ignore_file_path, "r") as ignore_file: for line in ignore_file.readlines(): - self.ignore_items.extend(self._pattern(line.strip(' \n'))) + self.ignore_items.extend(self._pattern(line.strip(" \n"))) def is_entry_ignored(self, relative_file: str) -> bool: def is_relative_file_ignored(cf_ignore_entry): - _logger.debug('is_relative_file_ignored - %s - %s', cf_ignore_entry, relative_file) - file_path = '/%s' % relative_file \ - if cf_ignore_entry.startswith('/') and not relative_file.startswith('/') \ - else relative_file + _logger.debug("is_relative_file_ignored - %s - %s", cf_ignore_entry, relative_file) + file_path = ( + "/%s" % relative_file if cf_ignore_entry.startswith("/") and not relative_file.startswith("/") else relative_file + ) return fnmatch.fnmatch(file_path, cf_ignore_entry) - return any([is_relative_file_ignored(ignore_item) - for ignore_item in self.ignore_items]) + return any([is_relative_file_ignored(ignore_item) for ignore_item in self.ignore_items]) @staticmethod def _pattern(pattern: str) -> List[str]: - if pattern.find('/') < 0: - return [pattern, os.path.join('**', pattern)] - elif pattern.endswith('/'): - return [os.path.join('/', pattern, '*'), - os.path.join('/', pattern, '**', '*'), - os.path.join('/', '**', pattern, '*'), - os.path.join('/', '**', pattern, '**', '*')] + if pattern.find("/") < 0: + return [pattern, os.path.join("**", pattern)] + elif pattern.endswith("/"): + return [ + os.path.join("/", pattern, "*"), + os.path.join("/", pattern, "**", "*"), + os.path.join("/", "**", pattern, "*"), + os.path.join("/", "**", pattern, "**", "*"), + ] else: - return [os.path.join('/', pattern), os.path.join('/', '**', pattern)] + return [os.path.join("/", pattern), os.path.join("/", "**", pattern)] diff --git a/main/cloudfoundry_client/operations/push/file_helper.py b/main/cloudfoundry_client/operations/push/file_helper.py index d9c187c..713fa0c 100644 --- a/main/cloudfoundry_client/operations/push/file_helper.py +++ b/main/cloudfoundry_client/operations/push/file_helper.py @@ -8,7 +8,7 @@ class FileHelper(object): @staticmethod def zip(file_location: str, directory_path: str, accept: Optional[Callable[[str], bool]] = None): - with zipfile.ZipFile(file_location, 'w', zipfile.ZIP_DEFLATED) as archive_out: + with zipfile.ZipFile(file_location, "w", zipfile.ZIP_DEFLATED) as archive_out: for dir_path, file_names in FileHelper.walk(directory_path): dir_full_location = os.path.join(directory_path, dir_path) if len(dir_path) > 0: @@ -16,12 +16,13 @@ def zip(file_location: str, directory_path: str, accept: Optional[Callable[[str] for file_name in file_names: file_relative_location = os.path.join(dir_path, file_name) if accept is None or accept(file_relative_location): - archive_out.write(os.path.join(dir_full_location, file_name), file_relative_location, - zipfile.ZIP_DEFLATED) + archive_out.write( + os.path.join(dir_full_location, file_name), file_relative_location, zipfile.ZIP_DEFLATED + ) @staticmethod def unzip(path: str, tmp_dir: str): - with zipfile.ZipFile(path, 'r') as zip_ref: + with zipfile.ZipFile(path, "r") as zip_ref: for entry in zip_ref.namelist(): filename = os.path.basename(entry) path_to_create = os.path.join(tmp_dir, entry) @@ -33,12 +34,12 @@ def unzip(path: str, tmp_dir: str): @staticmethod def walk(path: str) -> Generator[Tuple[str, List[str]], None, None]: for dir_path, _, files in os.walk(path, topdown=True): - yield dir_path[len(path):].lstrip('/'), files + yield dir_path[len(path) :].lstrip("/"), files @staticmethod def sha1(file_location: str) -> str: sha1 = hashlib.sha1() - with open(file_location, 'rb') as f: + with open(file_location, "rb") as f: while True: data = f.read(64 * 1024) if not data: @@ -53,4 +54,4 @@ def size(path: str) -> int: @staticmethod def mode(file_location: str) -> str: mode = str(oct(stat.S_IMODE(os.lstat(file_location).st_mode))) - return mode[len(mode) - 3:] + return mode[len(mode) - 3 :] diff --git a/main/cloudfoundry_client/operations/push/push.py b/main/cloudfoundry_client/operations/push/push.py index a04d72b..d4dd0c3 100644 --- a/main/cloudfoundry_client/operations/push/push.py +++ b/main/cloudfoundry_client/operations/push/push.py @@ -19,7 +19,7 @@ class PushOperation(object): UPLOAD_TIMEOUT = 15 * 60 - SPLIT_ROUTE_PATTERN = re.compile(r'(?P[a-z]+://)?(?P[^:/]+)(?P:\d+)?(?P/.*)?') + SPLIT_ROUTE_PATTERN = re.compile(r"(?P[a-z]+://)?(?P[^:/]+)(?P:\d+)?(?P/.*)?") def __init__(self, client: CloudFoundryClient): self.client = client @@ -29,7 +29,7 @@ def push(self, space_id: str, manifest_path: str, restart: bool = True): organization, space = self._retrieve_space_and_organization(space_id) for app_manifest in app_manifests: - if 'path' in app_manifest or 'docker' in app_manifest: + if "path" in app_manifest or "docker" in app_manifest: self._push_application(organization, space, app_manifest, restart) def _retrieve_space_and_organization(self, space_id: str) -> Tuple[Entity, Entity]: @@ -39,65 +39,74 @@ def _retrieve_space_and_organization(self, space_id: str) -> Tuple[Entity, Entit def _push_application(self, organization: Entity, space: Entity, app_manifest: dict, restart: bool): app = self._init_application(space, app_manifest) - self._route_application(organization, space, app, app_manifest.get('no-route', False), - app_manifest.get('routes', []), app_manifest.get('random-route', False)) - if 'path' in app_manifest: - self._upload_application(app, app_manifest['path']) - self._bind_services(space, app, app_manifest.get('services', [])) + self._route_application( + organization, + space, + app, + app_manifest.get("no-route", False), + app_manifest.get("routes", []), + app_manifest.get("random-route", False), + ) + if "path" in app_manifest: + self._upload_application(app, app_manifest["path"]) + self._bind_services(space, app, app_manifest.get("services", [])) if restart: PushOperation._restart_application(app) def _init_application(self, space: Entity, app_manifest: dict) -> Entity: - app = self.client.v2.apps.get_first(name=app_manifest['name'], space_guid=space['metadata']['guid']) - return self._update_application(app, app_manifest) if app is not None \ - else self._create_application(space, app_manifest) + app = self.client.v2.apps.get_first(name=app_manifest["name"], space_guid=space["metadata"]["guid"]) + return self._update_application(app, app_manifest) if app is not None else self._create_application(space, app_manifest) def _create_application(self, space: Entity, app_manifest: dict) -> Entity: - _logger.debug("Creating application %s", app_manifest['name']) + _logger.debug("Creating application %s", app_manifest["name"]) request = self._build_request_from_manifest(app_manifest) - request['environment_json'] = PushOperation._merge_environment(None, app_manifest) - request['space_guid'] = space['metadata']['guid'] - if request.get('health-check-type') == 'http' and request.get('health-check-http-endpoint') is None: - request['health-check-http-endpoint'] = '/' + request["environment_json"] = PushOperation._merge_environment(None, app_manifest) + request["space_guid"] = space["metadata"]["guid"] + if request.get("health-check-type") == "http" and request.get("health-check-http-endpoint") is None: + request["health-check-http-endpoint"] = "/" return self.client.v2.apps.create(**request) def _update_application(self, app: Entity, app_manifest: dict) -> Entity: - _logger.debug("Uploading application %s", app['entity']['name']) + _logger.debug("Uploading application %s", app["entity"]["name"]) request = self._build_request_from_manifest(app_manifest) - request['environment_json'] = PushOperation._merge_environment(app, app_manifest) - if request.get('health-check-type') == 'http' and request.get('health-check-http-endpoint') is None \ - and app['entity'].get('health_check_http_endpoint') is None: - request['health-check-http-endpoint'] = '/' - return self.client.v2.apps.update(app['metadata']['guid'], **request) + request["environment_json"] = PushOperation._merge_environment(app, app_manifest) + if ( + request.get("health-check-type") == "http" + and request.get("health-check-http-endpoint") is None + and app["entity"].get("health_check_http_endpoint") is None + ): + request["health-check-http-endpoint"] = "/" + return self.client.v2.apps.update(app["metadata"]["guid"], **request) def _build_request_from_manifest(self, app_manifest: dict) -> dict: request = dict() request.update(app_manifest) - stack = self.client.v2.stacks.get_first(name=app_manifest['stack']) if 'stack' in app_manifest else None + stack = self.client.v2.stacks.get_first(name=app_manifest["stack"]) if "stack" in app_manifest else None if stack is not None: - request['stack_guid'] = stack['metadata']['guid'] - docker = request.pop('docker', None) - if docker is not None and 'image' in docker: - request['docker_image'] = docker['image'] - request['diego'] = True - if 'username' in docker and 'password' in docker: - request['docker_credentials'] = dict(username=docker['username'], password=docker['password']) - buildpacks = request.pop('buildpacks', None) - if 'buildpack' not in request and buildpacks is not None and len(buildpacks) > 0: - request['buildpack'] = buildpacks[0] + request["stack_guid"] = stack["metadata"]["guid"] + docker = request.pop("docker", None) + if docker is not None and "image" in docker: + request["docker_image"] = docker["image"] + request["diego"] = True + if "username" in docker and "password" in docker: + request["docker_credentials"] = dict(username=docker["username"], password=docker["password"]) + buildpacks = request.pop("buildpacks", None) + if "buildpack" not in request and buildpacks is not None and len(buildpacks) > 0: + request["buildpack"] = buildpacks[0] return request @staticmethod def _merge_environment(app: Optional[Entity], app_manifest: dict) -> dict: environment = dict() - if app is not None and 'environment_json' in app['entity']: - environment.update(app['entity']['environment_json']) - if 'env' in app_manifest: - environment.update(app_manifest['env']) + if app is not None and "environment_json" in app["entity"]: + environment.update(app["entity"]["environment_json"]) + if "env" in app_manifest: + environment.update(app_manifest["env"]) return environment - def _route_application(self, organization: Entity, space: Entity, app: Entity, no_route: bool, routes: List[str], - random_route: bool): + def _route_application( + self, organization: Entity, space: Entity, app: Entity, no_route: bool, routes: List[str], random_route: bool + ): existing_routes = [route for route in app.routes()] if no_route: self._remove_all_routes(app, existing_routes) @@ -108,130 +117,138 @@ def _route_application(self, organization: Entity, space: Entity, app: Entity, n def _remove_all_routes(self, app: Entity, routes: List[Entity]): for route in routes: - self.client.v2.apps.remove_route(app['metadata']['guid'], route['metadata']['guid']) + self.client.v2.apps.remove_route(app["metadata"]["guid"], route["metadata"]["guid"]) def _build_default_route(self, space: Entity, app: Entity, random_route: bool): shared_domain = None for domain in self.client.v2.shared_domains.list(): - if not domain['entity'].get('internal', False): + if not domain["entity"].get("internal", False): shared_domain = domain break if shared_domain is None: - raise AssertionError('No route specified and no no-route field or shared domain') - if shared_domain['entity'].get('router_group_type') == 'tcp': - route = self.client.v2.routes.create_tcp_route(shared_domain['metadata']['guid'], - space['metadata']['guid']) + raise AssertionError("No route specified and no no-route field or shared domain") + if shared_domain["entity"].get("router_group_type") == "tcp": + route = self.client.v2.routes.create_tcp_route(shared_domain["metadata"]["guid"], space["metadata"]["guid"]) elif random_route: - route = self.client.v2.routes.create_host_route(shared_domain['metadata']['guid'], - space['metadata']['guid'], - self._to_host( - '%s-%d' % (app['entity']['name'], int(time.time())))) + route = self.client.v2.routes.create_host_route( + shared_domain["metadata"]["guid"], + space["metadata"]["guid"], + self._to_host("%s-%d" % (app["entity"]["name"], int(time.time()))), + ) else: - route = self.client.v2.routes.create_host_route(shared_domain['metadata']['guid'], - space['metadata']['guid'], - self._to_host(app['entity']['name'])) - self.client.v2.apps.associate_route(app['metadata']['guid'], route['metadata']['guid']) - - def _build_new_requested_routes(self, organization: Entity, space: Entity, app: Entity, - existing_routes: List[Entity], requested_routes: List[str]): - private_domains = {domain['entity']['name']: domain for domain in organization.private_domains()} - shared_domains = {domain['entity']['name']: domain for domain in self.client.v2.shared_domains.list()} + route = self.client.v2.routes.create_host_route( + shared_domain["metadata"]["guid"], space["metadata"]["guid"], self._to_host(app["entity"]["name"]) + ) + self.client.v2.apps.associate_route(app["metadata"]["guid"], route["metadata"]["guid"]) + + def _build_new_requested_routes( + self, organization: Entity, space: Entity, app: Entity, existing_routes: List[Entity], requested_routes: List[str] + ): + private_domains = {domain["entity"]["name"]: domain for domain in organization.private_domains()} + shared_domains = {domain["entity"]["name"]: domain for domain in self.client.v2.shared_domains.list()} for requested_route in requested_routes: route, port, path = PushOperation._split_route(requested_route) if len(path) > 0 and port is not None: _logger.error("Neither path nor port provided for route", requested_route) - raise AssertionError('Cannot set both port and path for route: %s' % requested_route) + raise AssertionError("Cannot set both port and path for route: %s" % requested_route) host, domain_name, domain = PushOperation._resolve_domain(route, private_domains, shared_domains) if port is not None and host is not None: - _logger.error('Host provided in route %s for tcp domain %s', requested_route, domain_name) + _logger.error("Host provided in route %s for tcp domain %s", requested_route, domain_name) raise AssertionError( - 'For route (%s) refers to domain %s that is a tcp one. It is hence routed by port and not by host' - % (requested_route, domain_name)) + "For route (%s) refers to domain %s that is a tcp one. It is hence routed by port and not by host" + % (requested_route, domain_name) + ) route_to_map = None - if port is not None and domain['entity'].get('router_group_type') != 'tcp': - _logger.error('Port provided in route %s for non tcp domain %s', requested_route, domain_name) - raise AssertionError('Cannot set port on route(%s) for non tcp domain' % requested_route) - elif domain['entity'].get('router_group_type') == 'tcp' and port is None: - _logger.error('No port provided in route %s for tcp domain %s', requested_route, domain_name) - raise AssertionError('Please specify a port on route (%s) for tcp domain' % requested_route) - elif domain['entity'].get('router_group_type') == 'tcp': - if not any([route['entity']['domain_guid'] == domain['metadata']['guid'] - and route['entity']['port'] == port] for route in existing_routes): + if port is not None and domain["entity"].get("router_group_type") != "tcp": + _logger.error("Port provided in route %s for non tcp domain %s", requested_route, domain_name) + raise AssertionError("Cannot set port on route(%s) for non tcp domain" % requested_route) + elif domain["entity"].get("router_group_type") == "tcp" and port is None: + _logger.error("No port provided in route %s for tcp domain %s", requested_route, domain_name) + raise AssertionError("Please specify a port on route (%s) for tcp domain" % requested_route) + elif domain["entity"].get("router_group_type") == "tcp": + if not any( + [route["entity"]["domain_guid"] == domain["metadata"]["guid"] and route["entity"]["port"] == port] + for route in existing_routes + ): route_to_map = self._resolve_new_tcp_route(space, domain, port) else: - if not any([route['entity']['domain_guid'] == domain['metadata']['guid'] - and route['entity']['host'] == host] for route in existing_routes): + if not any( + [route["entity"]["domain_guid"] == domain["metadata"]["guid"] and route["entity"]["host"] == host] + for route in existing_routes + ): route_to_map = self._resolve_new_host_route(space, domain, host, path) if route_to_map is not None: - _logger.debug('Associating route %s to application %s', requested_route, app['entity']['name']) - self.client.v2.apps.associate_route(app['metadata']['guid'], route_to_map['metadata']['guid']) + _logger.debug("Associating route %s to application %s", requested_route, app["entity"]["name"]) + self.client.v2.apps.associate_route(app["metadata"]["guid"], route_to_map["metadata"]["guid"]) def _resolve_new_host_route(self, space: Entity, domain: Entity, host: str, path: str) -> Entity: - existing_route = self.client.v2.routes.get_first(domain_guid=domain['metadata']['guid'], host=host, path=path) + existing_route = self.client.v2.routes.get_first(domain_guid=domain["metadata"]["guid"], host=host, path=path) if existing_route is None: - _logger.debug('Creating host route %s on domain %s and path %s', host, domain['entity']['name'], path) - existing_route = self.client.v2.routes.create_host_route(domain['metadata']['guid'], - space['metadata']['guid'], - host, - path) + _logger.debug("Creating host route %s on domain %s and path %s", host, domain["entity"]["name"], path) + existing_route = self.client.v2.routes.create_host_route( + domain["metadata"]["guid"], space["metadata"]["guid"], host, path + ) else: - _logger.debug('Host route %s on domain %s and path %s already exists with guid %s', - host, - domain['entity']['name'], - path, - existing_route['metadata']['guid']) + _logger.debug( + "Host route %s on domain %s and path %s already exists with guid %s", + host, + domain["entity"]["name"], + path, + existing_route["metadata"]["guid"], + ) return existing_route def _resolve_new_tcp_route(self, space: Entity, domain: Entity, port: int) -> Entity: - existing_route = self.client.v2.routes.get_first(domain_guid=domain['metadata']['guid'], port=port) + existing_route = self.client.v2.routes.get_first(domain_guid=domain["metadata"]["guid"], port=port) if existing_route is None: - _logger.debug('Creating tcp route %d on domain %s', port, domain['entity']['name']) - existing_route = self.client.v2.routes.create_tcp_route(domain['metadata']['guid'], - space['metadata']['guid'], - port) + _logger.debug("Creating tcp route %d on domain %s", port, domain["entity"]["name"]) + existing_route = self.client.v2.routes.create_tcp_route(domain["metadata"]["guid"], space["metadata"]["guid"], port) else: - _logger.debug('TCP route %d on domain %s already exists with guid %s', - port, - domain['entity']['name'], - existing_route['metadata']['guid']) + _logger.debug( + "TCP route %d on domain %s already exists with guid %s", + port, + domain["entity"]["name"], + existing_route["metadata"]["guid"], + ) return existing_route @staticmethod def _split_route(requested_route: Dict[str, str]) -> Tuple[str, int, str]: - route_splitted = PushOperation.SPLIT_ROUTE_PATTERN.match(requested_route['route']) + route_splitted = PushOperation.SPLIT_ROUTE_PATTERN.match(requested_route["route"]) if route_splitted is None: - raise AssertionError('Invalid route: %s' % requested_route['route']) - domain = route_splitted.group('domain') - port = route_splitted.group('port') - path = route_splitted.group('path') - return domain, int(port[1:]) if port is not None else None, '' if path is None or path == '/' else path + raise AssertionError("Invalid route: %s" % requested_route["route"]) + domain = route_splitted.group("domain") + port = route_splitted.group("port") + path = route_splitted.group("path") + return domain, int(port[1:]) if port is not None else None, "" if path is None or path == "/" else path @staticmethod - def _resolve_domain(route: str, private_domains: Dict[str, Entity], shared_domains: Dict[str, Entity]) -> Tuple[ - str, str, Entity]: + def _resolve_domain( + route: str, private_domains: Dict[str, Entity], shared_domains: Dict[str, Entity] + ) -> Tuple[str, str, Entity]: for domains in [private_domains, shared_domains]: if route in domains: - return '', route, domains[route] + return "", route, domains[route] else: - idx = route.find('.') + idx = route.find(".") if 0 < idx < (len(route) - 2): host = route[:idx] - domain = route[idx + 1:] + domain = route[idx + 1 :] if domain in domains: return host, domain, domains[domain] - raise AssertionError('Cannot find domain for route %s' % route) + raise AssertionError("Cannot find domain for route %s" % route) def _upload_application(self, app: Entity, application_path: str) -> Entity: - _logger.debug('Uploading application %s', app['entity']['name']) + _logger.debug("Uploading application %s", app["entity"]["name"]) if os.path.isfile(application_path): self._upload_application_zip(app, application_path) elif os.path.isdir(application_path): self._upload_application_directory(app, application_path) else: - raise AssertionError('Path %s is neither a directory nor a file' % application_path) + raise AssertionError("Path %s is neither a directory nor a file" % application_path) def _upload_application_zip(self, app: Entity, path: str): - _logger.debug('Unzipping file %s', path) + _logger.debug("Unzipping file %s", path) tmp_dir = tempfile.mkdtemp() try: FileHelper.unzip(path, tmp_dir) @@ -240,42 +257,44 @@ def _upload_application_zip(self, app: Entity, path: str): shutil.rmtree(tmp_dir) def _upload_application_directory(self, app: Entity, application_path: str): - _logger.debug('Uploading application from directory %s', application_path) + _logger.debug("Uploading application from directory %s", application_path) _, temp_file = tempfile.mkstemp() try: resource_descriptions_by_path = PushOperation._load_all_resources(application_path) def generate_key(item: dict): - return '%s-%d' % (item["sha1"], item["size"]) - - already_uploaded_entries = [generate_key(item) for item in - self.client.v2.resources.match([dict(sha1=item["sha1"], size=item["size"]) - for item in - resource_descriptions_by_path.values()])] - _logger.debug('Already uploaded %d / %d items', - len(already_uploaded_entries), len(resource_descriptions_by_path)) - - FileHelper.zip(temp_file, application_path, - lambda item: item in resource_descriptions_by_path - and generate_key( - resource_descriptions_by_path[item]) not in already_uploaded_entries) - _logger.debug('Diff zip file built: %s', temp_file) + return "%s-%d" % (item["sha1"], item["size"]) + + already_uploaded_entries = [ + generate_key(item) + for item in self.client.v2.resources.match( + [dict(sha1=item["sha1"], size=item["size"]) for item in resource_descriptions_by_path.values()] + ) + ] + _logger.debug("Already uploaded %d / %d items", len(already_uploaded_entries), len(resource_descriptions_by_path)) + + FileHelper.zip( + temp_file, + application_path, + lambda item: item in resource_descriptions_by_path + and generate_key(resource_descriptions_by_path[item]) not in already_uploaded_entries, + ) + _logger.debug("Diff zip file built: %s", temp_file) resources = [ - dict(fn=resource_path, - sha1=resource_description["sha1"], - size=resource_description["size"], - mode=resource_description["mode"]) + dict( + fn=resource_path, + sha1=resource_description["sha1"], + size=resource_description["size"], + mode=resource_description["mode"], + ) for resource_path, resource_description in resource_descriptions_by_path.items() if generate_key(resource_description) in already_uploaded_entries ] - _logger.debug('Uploading bits of application') - job = self.client.v2.apps.upload(app['metadata']['guid'], - resources, - temp_file, - True) + _logger.debug("Uploading bits of application") + job = self.client.v2.apps.upload(app["metadata"]["guid"], resources, temp_file, True) self._poll_job(job) finally: - _logger.debug('Skipping remove of zip file') + _logger.debug("Skipping remove of zip file") @staticmethod def _load_all_resources(top_directory: str) -> dict: @@ -289,47 +308,51 @@ def _load_all_resources(top_directory: str) -> dict: application_items[relative_file_location] = dict( sha1=FileHelper.sha1(absolute_file_location), size=FileHelper.size(absolute_file_location), - mode=FileHelper.mode(absolute_file_location)) + mode=FileHelper.mode(absolute_file_location), + ) return application_items def _bind_services(self, space: Entity, app: Entity, services: List[str]): - service_instances = [service_instance for service_instance in space.service_instances( - return_user_provided_service_instances="true")] - service_name_to_instance_guid = {service_instance["entity"]["name"]: service_instance["metadata"]["guid"] - for service_instance in service_instances} - existing_service_instance_guid = [service_binding['entity']['service_instance_guid'] - for service_binding in app.service_bindings()] + service_instances = [ + service_instance for service_instance in space.service_instances(return_user_provided_service_instances="true") + ] + service_name_to_instance_guid = { + service_instance["entity"]["name"]: service_instance["metadata"]["guid"] for service_instance in service_instances + } + existing_service_instance_guid = [ + service_binding["entity"]["service_instance_guid"] for service_binding in app.service_bindings() + ] for service_name in services: service_instance_guid = service_name_to_instance_guid.get(service_name) if service_instance_guid is None: - raise AssertionError('No service found with name %s' % service_name) + raise AssertionError("No service found with name %s" % service_name) elif service_instance_guid in existing_service_instance_guid: - _logger.debug('%s already bound to %s', app["entity"]["name"], service_name) + _logger.debug("%s already bound to %s", app["entity"]["name"], service_name) else: - _logger.debug('Binding %s to %s', app["entity"]["name"], service_name) - self.client.v2.service_bindings.create(app['metadata']['guid'], service_instance_guid) + _logger.debug("Binding %s to %s", app["entity"]["name"], service_name) + self.client.v2.service_bindings.create(app["metadata"]["guid"], service_instance_guid) def _poll_job(self, job: Entity): def job_not_ended(j): - return j['entity']['status'] in ['queued', 'running'] + return j["entity"]["status"] in ["queued", "running"] - job_guid = job['metadata']['guid'] - _logger.debug('Waiting for upload of application to be complete. Polling job %s...', job_guid) + job_guid = job["metadata"]["guid"] + _logger.debug("Waiting for upload of application to be complete. Polling job %s...", job_guid) started_time = time.time() elapsed_time = 0 while job_not_ended(job) and elapsed_time < PushOperation.UPLOAD_TIMEOUT: - _logger.debug('Getting job status %s..', job_guid) + _logger.debug("Getting job status %s..", job_guid) job = self.client.v2.jobs.get(job_guid) if job_not_ended(job): time.sleep(5) elapsed_time = int(time.time() - started_time) if job_not_ended(job): - raise AssertionError('Exceeded timeout while polling job of upload') - elif job['entity']['status'] == 'failed': - raise AssertionError('Job of upload exceeded in error: %s', json.dumps(job['entity']['error_details'])) + raise AssertionError("Exceeded timeout while polling job of upload") + elif job["entity"]["status"] == "failed": + raise AssertionError("Job of upload exceeded in error: %s", json.dumps(job["entity"]["error_details"])) else: - _logger.debug('Job ended with status %s', job['entity']['status']) + _logger.debug("Job ended with status %s", job["entity"]["status"]) @staticmethod def _restart_application(app: Entity): @@ -340,7 +363,7 @@ def _restart_application(app: Entity): @staticmethod def _to_host(host: str) -> str: def no_space(h: str) -> str: - return re.sub(r'[\s_]+', "-", h) + return re.sub(r"[\s_]+", "-", h) def only_alphabetical_and_hyphen(h: str) -> str: return re.sub("[^a-z0-9-]", "", h) diff --git a/main/cloudfoundry_client/operations/push/validation/manifest.py b/main/cloudfoundry_client/operations/push/validation/manifest.py index 7ad44ff..de4666e 100644 --- a/main/cloudfoundry_client/operations/push/validation/manifest.py +++ b/main/cloudfoundry_client/operations/push/validation/manifest.py @@ -8,36 +8,36 @@ class ManifestReader(object): MEMORY_PATTERN = re.compile(r"^(\d+)([KMGT])B?$") - POSITIVE_FIELDS = ['instances', 'timeout'] + POSITIVE_FIELDS = ["instances", "timeout"] - BOOLEAN_FIELDS = ['no-route', 'random-route'] + BOOLEAN_FIELDS = ["no-route", "random-route"] @staticmethod def load_application_manifests(manifest_path: str): - with open(manifest_path, 'r') as fp: + with open(manifest_path, "r") as fp: manifest = yaml.safe_load(fp) if manifest is None: - raise AssertionError('No valid yaml document found') + raise AssertionError("No valid yaml document found") ManifestReader._validate_manifest(os.path.dirname(manifest_path), manifest) - return manifest['applications'] + return manifest["applications"] @staticmethod def _validate_manifest(manifest_directory: str, manifest: dict): - for app_manifest in manifest['applications']: + for app_manifest in manifest["applications"]: ManifestReader._validate_application_manifest(manifest_directory, app_manifest) @staticmethod def _validate_application_manifest(manifest_directory: str, app_manifest: dict): - name = app_manifest.get('name') + name = app_manifest.get("name") if name is None or len(name) == 0: - raise AssertionError('name must be set') - docker_manifest = app_manifest.get('docker') + raise AssertionError("name must be set") + docker_manifest = app_manifest.get("docker") if docker_manifest is not None: - if app_manifest.get('path') is not None: - raise AssertionError('Both path and docker cannot be set') + if app_manifest.get("path") is not None: + raise AssertionError("Both path and docker cannot be set") ManifestReader._validate_application_docker(docker_manifest) - elif 'path' not in app_manifest: - raise AssertionError('One of path or docker must be set') + elif "path" not in app_manifest: + raise AssertionError("One of path or docker must be set") else: ManifestReader._absolute_path(manifest_directory, app_manifest) ManifestReader._convert_memory(app_manifest) @@ -51,83 +51,88 @@ def _validate_application_manifest(manifest_directory: str, app_manifest: dict): @staticmethod def _check_deprecated_attributes(app_manifest: dict): - if app_manifest.get('hosts') is not None or app_manifest.get('host') \ - or app_manifest.get('domains') is not None or app_manifest.get('domain') \ - or app_manifest.get('no-hostname') is not None: - raise AssertionError( - 'hosts, host, domains, domain and no-hostname are all deprecated. Use the routes attribute') + if ( + app_manifest.get("hosts") is not None + or app_manifest.get("host") + or app_manifest.get("domains") is not None + or app_manifest.get("domain") + or app_manifest.get("no-hostname") is not None + ): + raise AssertionError("hosts, host, domains, domain and no-hostname are all deprecated. Use the routes attribute") @staticmethod def _convert_memory(manifest: dict): - if 'memory' in manifest: - memory = manifest['memory'].upper() + if "memory" in manifest: + memory = manifest["memory"].upper() match = ManifestReader.MEMORY_PATTERN.match(memory) if match is None: raise AssertionError("Invalid memory format: %s" % memory) memory_number = int(match.group(1)) - if match.group(2) == 'K': + if match.group(2) == "K": memory_number *= 1024 - elif match.group(2) == 'M': + elif match.group(2) == "M": memory_number *= 1024 * 1024 - elif match.group(2) == 'G': + elif match.group(2) == "G": memory_number *= 1024 * 1024 * 1024 - elif match.group(2) == 'T': + elif match.group(2) == "T": memory_number *= 1024 * 1024 * 1024 * 1024 else: - raise AssertionError('Invalid memory unit: %s' % memory) - manifest['memory'] = int(memory_number / (1024 * 1024)) + raise AssertionError("Invalid memory unit: %s" % memory) + manifest["memory"] = int(memory_number / (1024 * 1024)) @staticmethod def _convert_positive(manifest: dict, field: str): if field in manifest: value = int(manifest[field]) if value < 1: - raise AssertionError("Invalid %s value: %s. It ust be positive" % (field, s)) + raise AssertionError("Invalid %s value: %s. It ust be positive" % (field, value)) manifest[field] = value @staticmethod def _convert_boolean(manifest: dict, field: str): if field in manifest: field_value = manifest[field] - manifest[field] = field_value if type(field_value) == bool else field_value.lower() == 'true' + manifest[field] = field_value if type(field_value) == bool else field_value.lower() == "true" @staticmethod def _validate_routes(manifest: dict): - for route in manifest.get('routes', []): - if type(route) != dict or 'route' not in route: - raise AssertionError('routes attribute must be a list of object containing a route attribute') + for route in manifest.get("routes", []): + if type(route) != dict or "route" not in route: + raise AssertionError("routes attribute must be a list of object containing a route attribute") @staticmethod def _validate_application_docker(docker_manifest: dict): - docker_image = docker_manifest.get('image') - if docker_image is not None and docker_manifest.get('buildpack') is not None: - raise AssertionError('image and buildpack can not both be set for docker') - docker_username = docker_manifest.get('username') - docker_password = docker_manifest.get('password') + docker_image = docker_manifest.get("image") + if docker_image is not None and docker_manifest.get("buildpack") is not None: + raise AssertionError("image and buildpack can not both be set for docker") + docker_username = docker_manifest.get("username") + docker_password = docker_manifest.get("password") if docker_username is not None and docker_password is None or docker_username is None and docker_password is not None: - raise AssertionError('Docker username/password must be set together or both be unset') + raise AssertionError("Docker username/password must be set together or both be unset") if docker_username is not None and docker_password is not None and docker_image is None: - raise AssertionError('Docker image not set while docker username/password are set') + raise AssertionError("Docker image not set while docker username/password are set") @staticmethod def _absolute_path(manifest_directory: str, manifest: dict): - if 'path' in manifest: - path = manifest['path'] + if "path" in manifest: + path = manifest["path"] if path == os.path.abspath(path): - manifest['path'] = path - elif manifest_directory == '' or manifest_directory == '.': - manifest['path'] = os.path.abspath(path) + manifest["path"] = path + elif manifest_directory == "" or manifest_directory == ".": + manifest["path"] = os.path.abspath(path) else: - manifest['path'] = os.path.abspath(os.path.join(manifest_directory, path)) + manifest["path"] = os.path.abspath(os.path.join(manifest_directory, path)) @staticmethod def _convert_environment(app_manifest: dict): - environment = app_manifest.get('env', None) + environment = app_manifest.get("env", None) if environment is not None: if type(environment) != dict: raise AssertionError("'env' entry must be a dictionary") - app_manifest['env'] = {key: json.dumps(value) for key, value in environment.items() if - value is not None and type(value) != str} - app_manifest['env'].update( - {key: value for key, value in environment.items() if value is not None and type(value) == str}) + app_manifest["env"] = { + key: json.dumps(value) for key, value in environment.items() if value is not None and type(value) != str + } + app_manifest["env"].update( + {key: value for key, value in environment.items() if value is not None and type(value) == str} + ) diff --git a/main/cloudfoundry_client/request_object.py b/main/cloudfoundry_client/request_object.py index 19fc851..f0d2152 100644 --- a/main/cloudfoundry_client/request_object.py +++ b/main/cloudfoundry_client/request_object.py @@ -1,4 +1,4 @@ class Request(dict): def __setitem__(self, key, value): if value is not None: - super(Request, self).__setitem__(key, value) \ No newline at end of file + super(Request, self).__setitem__(key, value) diff --git a/main/cloudfoundry_client/rlpgateway/client.py b/main/cloudfoundry_client/rlpgateway/client.py index 72dc1c2..8954597 100644 --- a/main/cloudfoundry_client/rlpgateway/client.py +++ b/main/cloudfoundry_client/rlpgateway/client.py @@ -52,9 +52,7 @@ async def stream_logs(self, app_guid, **kwargs): log_message = buffer.split(b"\n\n")[0] buffer = buffer.replace(log_message + b"\n\n", b"") yield log_message - elif buffer.startswith( - b"event: heartbeat" - ) or buffer.startswith(b"event: closing"): + elif buffer.startswith(b"event: heartbeat") or buffer.startswith(b"event: closing"): # Consume heartbeats to keep the connection alive buffer = b"" yield data diff --git a/main/cloudfoundry_client/v2/apps.py b/main/cloudfoundry_client/v2/apps.py index 16c0515..64a93a0 100644 --- a/main/cloudfoundry_client/v2/apps.py +++ b/main/cloudfoundry_client/v2/apps.py @@ -3,101 +3,134 @@ import os from http import HTTPStatus from time import sleep -from typing import Optional, Dict +from typing import Optional, Dict, TYPE_CHECKING from cloudfoundry_client.doppler.client import EnvelopeStream from cloudfoundry_client.errors import InvalidStatusCode from cloudfoundry_client.json_object import JsonObject from cloudfoundry_client.v2.entities import Entity, EntityManager, PaginateEntities +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + _logger = logging.getLogger(__name__) class Application(Entity): def instances(self) -> Dict[str, JsonObject]: - return self.client.v2.apps.get_instances(self['metadata']['guid']) + return self.client.v2.apps.get_instances(self["metadata"]["guid"]) - def start(self) -> 'Application': - return self.client.v2.apps.start(self['metadata']['guid']) + def start(self) -> "Application": + return self.client.v2.apps.start(self["metadata"]["guid"]) - def stop(self) -> 'Application': - return self.client.v2.apps.stop(self['metadata']['guid']) + def stop(self) -> "Application": + return self.client.v2.apps.stop(self["metadata"]["guid"]) def restart_instance(self, instance_id: int): - return self.client.v2.apps.restart_instance(self['metadata']['guid'], instance_id) + return self.client.v2.apps.restart_instance(self["metadata"]["guid"], instance_id) def stats(self) -> Dict[str, JsonObject]: - return self.client.v2.apps.get_stats(self['metadata']['guid']) + return self.client.v2.apps.get_stats(self["metadata"]["guid"]) def env(self) -> Dict[str, JsonObject]: - return self.client.v2.apps.get_env(self['metadata']['guid']) + return self.client.v2.apps.get_env(self["metadata"]["guid"]) def summary(self) -> JsonObject: - return self.client.v2.apps.get_summary(self['metadata']['guid']) + return self.client.v2.apps.get_summary(self["metadata"]["guid"]) - def restage(self) -> 'Application': - return self.client.v2.apps.restage(self['metadata']['guid']) + def restage(self) -> "Application": + return self.client.v2.apps.restage(self["metadata"]["guid"]) def recent_logs(self) -> EnvelopeStream: - return self.client.doppler.recent_logs(self['metadata']['guid']) + return self.client.doppler.recent_logs(self["metadata"]["guid"]) def stream_logs(self) -> EnvelopeStream: - return self.client.doppler.stream_logs(self['metadata']['guid']) + return self.client.doppler.stream_logs(self["metadata"]["guid"]) class AppManager(EntityManager): - APPLICATION_FIELDS = ['name', 'memory', 'instances', 'disk_quota', 'space_guid', 'stack_guid', 'state', 'command', - 'buildpack', 'health_check_http_endpoint', 'health_check_type', 'health_check_timeout', - 'diego', 'enable_ssh', 'docker_image', 'docker_credentials', 'environment_json', 'production', - 'console', 'debug', 'staging_failed_reason', 'staging_failed_description', 'ports'] - - def __init__(self, target_endpoint: str, client: 'CloudFoundryClient'): - super(AppManager, self).__init__(target_endpoint, client, '/v2/apps', - lambda pairs: Application(target_endpoint, client, pairs)) + APPLICATION_FIELDS = [ + "name", + "memory", + "instances", + "disk_quota", + "space_guid", + "stack_guid", + "state", + "command", + "buildpack", + "health_check_http_endpoint", + "health_check_type", + "health_check_timeout", + "diego", + "enable_ssh", + "docker_image", + "docker_credentials", + "environment_json", + "production", + "console", + "debug", + "staging_failed_reason", + "staging_failed_description", + "ports", + ] + + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(AppManager, self).__init__( + target_endpoint, client, "/v2/apps", lambda pairs: Application(target_endpoint, client, pairs) + ) def get_stats(self, application_guid: str) -> Dict[str, JsonObject]: - return self._get('%s/%s/stats' % (self.entity_uri, application_guid), JsonObject) + return self._get("%s/%s/stats" % (self.entity_uri, application_guid), JsonObject) def get_instances(self, application_guid: str) -> Dict[str, JsonObject]: - return self._get('%s/%s/instances' % (self.entity_uri, application_guid), JsonObject) + return self._get("%s/%s/instances" % (self.entity_uri, application_guid), JsonObject) def get_env(self, application_guid: str) -> Dict[str, JsonObject]: - return self._get('%s/%s/env' % (self.entity_uri, application_guid), JsonObject) + return self._get("%s/%s/env" % (self.entity_uri, application_guid), JsonObject) def get_summary(self, application_guid: str) -> JsonObject: - return self._get('%s/%s/summary' % (self.entity_uri, application_guid), JsonObject) + return self._get("%s/%s/summary" % (self.entity_uri, application_guid), JsonObject) def associate_route(self, application_guid: str, route_guid: str) -> Application: - self._put('%s%s/%s/routes/%s' % (self.target_endpoint, self.entity_uri, application_guid, route_guid)) + self._put("%s%s/%s/routes/%s" % (self.target_endpoint, self.entity_uri, application_guid, route_guid)) def list_routes(self, application_guid: str, **kwargs) -> PaginateEntities: - return self.client.v2.routes._list('%s/%s/routes' % (self.entity_uri, application_guid), **kwargs) + return self.client.v2.routes._list("%s/%s/routes" % (self.entity_uri, application_guid), **kwargs) def remove_route(self, application_guid: str, route_guid: str): - self._delete('%s%s/%s/routes/%s' % (self.target_endpoint, self.entity_uri, application_guid, route_guid)) + self._delete("%s%s/%s/routes/%s" % (self.target_endpoint, self.entity_uri, application_guid, route_guid)) def list_service_bindings(self, application_guid: str, **kwargs) -> PaginateEntities: - return self.client.v2.service_bindings._list('%s/%s/service_bindings' % (self.entity_uri, application_guid), - **kwargs) - - def start(self, application_guid: str, check_time: Optional[float] = 0.5, timeout: Optional[float] = 300.0, - asynchronous: Optional[bool] = False) -> Application: - result = super(AppManager, self)._update(application_guid, - dict(state='STARTED')) + return self.client.v2.service_bindings._list("%s/%s/service_bindings" % (self.entity_uri, application_guid), **kwargs) + + def start( + self, + application_guid: str, + check_time: Optional[float] = 0.5, + timeout: Optional[float] = 300.0, + asynchronous: Optional[bool] = False, + ) -> Application: + result = super(AppManager, self)._update(application_guid, dict(state="STARTED")) if asynchronous: return result else: summary = self.get_summary(application_guid) - self._wait_for_instances_in_state(application_guid, summary['instances'], 'RUNNING', check_time, timeout) + self._wait_for_instances_in_state(application_guid, summary["instances"], "RUNNING", check_time, timeout) return result - def stop(self, application_guid: str, check_time: Optional[float] = 0.5, timeout: Optional[float] = 500.0, - asynchronous: Optional[bool] = False) -> Application: - result = super(AppManager, self)._update(application_guid, dict(state='STOPPED')) + def stop( + self, + application_guid: str, + check_time: Optional[float] = 0.5, + timeout: Optional[float] = 500.0, + asynchronous: Optional[bool] = False, + ) -> Application: + result = super(AppManager, self)._update(application_guid, dict(state="STOPPED")) if asynchronous: return result else: - self._wait_for_instances_in_state(application_guid, 0, 'STOPPED', check_time, timeout) + self._wait_for_instances_in_state(application_guid, 0, "STOPPED", check_time, timeout) return result def restart_instance(self, application_guid: str, instance_id: int): @@ -107,8 +140,8 @@ def restage(self, application_guid: str) -> Application: return self._post("%s%s/%s/restage" % (self.target_endpoint, self.entity_uri, application_guid)) def create(self, **kwargs) -> Application: - if kwargs.get('name') is None or kwargs.get('space_guid') is None: - raise AssertionError('Please provide a name and a space_guid') + if kwargs.get("name") is None or kwargs.get("space_guid") is None: + raise AssertionError("Please provide a name and a space_guid") request = AppManager._generate_application_update_request(**kwargs) return super(AppManager, self)._create(request) @@ -121,39 +154,44 @@ def remove(self, application_guid: str): def upload(self, application_guid: str, resources, application: str, asynchronous: Optional[bool] = False): application_size = os.path.getsize(application) - with open(application, 'rb') as binary_file: - return self.client.put("%s%s/%s/bits" % (self.target_endpoint, self.entity_uri, application_guid), - params={"async": "true" if asynchronous else "false"} if asynchronous else None, - data=dict(resources=json.dumps(resources)), - files=dict(application=('application.zip', - binary_file, - 'application/zip', - {'Content-Length': application_size, - 'Content-Transfer-Encoding': 'binary'}))) \ - .json(object_pairs_hook=JsonObject) + with open(application, "rb") as binary_file: + return self.client.put( + "%s%s/%s/bits" % (self.target_endpoint, self.entity_uri, application_guid), + params={"async": "true" if asynchronous else "false"} if asynchronous else None, + data=dict(resources=json.dumps(resources)), + files=dict( + application=( + "application.zip", + binary_file, + "application/zip", + {"Content-Length": application_size, "Content-Transfer-Encoding": "binary"}, + ) + ), + ).json(object_pairs_hook=JsonObject) @staticmethod def _generate_application_update_request(**kwargs) -> dict: return {key: kwargs[key] for key in AppManager.APPLICATION_FIELDS if key in kwargs} - def _wait_for_instances_in_state(self, application_guid: str, number_required: int, state_expected: str, - check_time: float, timeout: float): + def _wait_for_instances_in_state( + self, application_guid: str, number_required: int, state_expected: str, check_time: float, timeout: float + ): all_in_expected_state = False sum_waiting = 0 while not all_in_expected_state: instances = self._safe_get_instances(application_guid) number_in_expected_state = 0 for instance_number, instance in list(instances.items()): - if instance['state'] == state_expected: + if instance["state"] == state_expected: number_in_expected_state += 1 # this case will make this code work for both stop and start operation all_in_expected_state = number_in_expected_state == number_required if not all_in_expected_state: - _logger.debug('_wait_for_instances_in_state - %d/%d %s', number_in_expected_state, - number_required, - state_expected) + _logger.debug( + "_wait_for_instances_in_state - %d/%d %s", number_in_expected_state, number_required, state_expected + ) if sum_waiting > timeout: - raise AssertionError('Failed to get state %s for %d instances' % (state_expected, number_required)) + raise AssertionError("Failed to get state %s for %d instances" % (state_expected, number_required)) sleep(check_time) sum_waiting += check_time @@ -162,7 +200,7 @@ def _safe_get_instances(self, application_guid: str) -> JsonObject: return self.get_instances(application_guid) except InvalidStatusCode as ex: if ex.status_code == HTTPStatus.BAD_REQUEST and type(ex.body) == dict: - code = ex.body.get('code', -1) + code = ex.body.get("code", -1) # 170002: staging not finished # 220001: instances error if code == 220001 or code == 170002: diff --git a/main/cloudfoundry_client/v2/buildpacks.py b/main/cloudfoundry_client/v2/buildpacks.py index 800572e..958a283 100644 --- a/main/cloudfoundry_client/v2/buildpacks.py +++ b/main/cloudfoundry_client/v2/buildpacks.py @@ -1,9 +1,14 @@ +from typing import TYPE_CHECKING + from cloudfoundry_client.v2.entities import EntityManager, Entity +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + class BuildpackManager(EntityManager): - def __init__(self, target_endpoint: str, client: 'CloudFoundryClient'): - super(BuildpackManager, self).__init__(target_endpoint, client, '/v2/buildpacks') + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(BuildpackManager, self).__init__(target_endpoint, client, "/v2/buildpacks") def update(self, buildpack_guid: str, parameters: dict) -> Entity: return super(BuildpackManager, self)._update(buildpack_guid, parameters) diff --git a/main/cloudfoundry_client/v2/entities.py b/main/cloudfoundry_client/v2/entities.py index cbaf34b..710de5d 100644 --- a/main/cloudfoundry_client/v2/entities.py +++ b/main/cloudfoundry_client/v2/entities.py @@ -1,40 +1,39 @@ import logging from functools import partial, reduce -from typing import Callable, List, Tuple, Any, Optional, Generator +from typing import Callable, List, Tuple, Any, Optional, Generator, TYPE_CHECKING from urllib.parse import quote - from requests import Response from cloudfoundry_client.errors import InvalidEntity from cloudfoundry_client.json_object import JsonObject from cloudfoundry_client.request_object import Request +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + _logger = logging.getLogger(__name__) class Entity(JsonObject): - def __init__(self, target_endpoint: str, client: 'CloudFoundryClient', *args, **kwargs): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient", *args, **kwargs): super(Entity, self).__init__(*args, **kwargs) self.target_endpoint = target_endpoint self.client = client try: - if not (isinstance(self.get('entity'), dict)): + if not (isinstance(self.get("entity"), dict)): raise InvalidEntity(**self) - for attribute, value in list(self['entity'].items()): - domain_name, suffix = attribute.rpartition('_')[::2] - if suffix == 'url': - manager_name = domain_name if domain_name.endswith('s') else '%ss' % domain_name + for attribute, value in list(self["entity"].items()): + domain_name, suffix = attribute.rpartition("_")[::2] + if suffix == "url": + manager_name = domain_name if domain_name.endswith("s") else "%ss" % domain_name try: other_manager = getattr(client.v2, manager_name) except AttributeError: # generic manager - other_manager = EntityManager( - target_endpoint, - client, - '') - if domain_name.endswith('s'): + other_manager = EntityManager(target_endpoint, client, "") + if domain_name.endswith("s"): new_method = partial(other_manager._list, value) else: new_method = partial(other_manager._get, value) @@ -50,65 +49,66 @@ def __init__(self, target_endpoint: str, client: 'CloudFoundryClient', *args, ** class EntityManager(object): - list_query_parameters = ['page', 'results-per-page', 'order-direction'] + list_query_parameters = ["page", "results-per-page", "order-direction"] - list_multi_parameters = ['order-by'] + list_multi_parameters = ["order-by"] - def __init__(self, target_endpoint: str, client: 'CloudFoundryClient', entity_uri: str, - entity_builder: Optional[EntityBuilder] = None): + def __init__( + self, target_endpoint: str, client: "CloudFoundryClient", entity_uri: str, entity_builder: Optional[EntityBuilder] = None + ): self.target_endpoint = target_endpoint self.entity_uri = entity_uri self.client = client - self.entity_builder = entity_builder if entity_builder is not None else lambda pairs: Entity(target_endpoint, - client, pairs) + self.entity_builder = ( + entity_builder if entity_builder is not None else lambda pairs: Entity(target_endpoint, client, pairs) + ) def _get(self, requested_path: str, entity_builder: Optional[EntityBuilder] = None) -> Entity: - url = '%s%s' % (self.target_endpoint, requested_path) + url = "%s%s" % (self.target_endpoint, requested_path) response = self.client.get(url) - _logger.debug('GET - %s - %s', requested_path, response.text) + _logger.debug("GET - %s - %s", requested_path, response.text) return self._read_response(response, entity_builder) - def _list(self, requested_path: str, entity_builder: Optional[EntityBuilder] = None, **kwargs) \ - -> PaginateEntities: - url_requested = self._get_url_filtered('%s%s' % (self.target_endpoint, requested_path), **kwargs) + def _list(self, requested_path: str, entity_builder: Optional[EntityBuilder] = None, **kwargs) -> PaginateEntities: + url_requested = self._get_url_filtered("%s%s" % (self.target_endpoint, requested_path), **kwargs) response = self.client.get(url_requested) entity_builder = self._get_entity_builder(entity_builder) while True: - _logger.debug('GET - %s - %s', url_requested, response.text) + _logger.debug("GET - %s - %s", url_requested, response.text) response_json = self._read_response(response, JsonObject) - for resource in response_json['resources']: + for resource in response_json["resources"]: yield entity_builder(list(resource.items())) - if response_json['next_url'] is None: + if response_json["next_url"] is None: break else: - url_requested = '%s%s' % (self.target_endpoint, response_json['next_url']) + url_requested = "%s%s" % (self.target_endpoint, response_json["next_url"]) response = self.client.get(url_requested) def _create(self, data: dict, **kwargs) -> Entity: - url = '%s%s' % (self.target_endpoint, self.entity_uri) + url = "%s%s" % (self.target_endpoint, self.entity_uri) return self._post(url, data, **kwargs) def _update(self, resource_id: str, data: dict, **kwargs): - url = '%s%s/%s' % (self.target_endpoint, self.entity_uri, resource_id) + url = "%s%s/%s" % (self.target_endpoint, self.entity_uri, resource_id) return self._put(url, data, **kwargs) def _remove(self, resource_id: str, **kwargs): - url = '%s%s/%s' % (self.target_endpoint, self.entity_uri, resource_id) + url = "%s%s/%s" % (self.target_endpoint, self.entity_uri, resource_id) self._delete(url, **kwargs) def _post(self, url: str, data: Optional[dict] = None, **kwargs): response = self.client.post(url, json=data, **kwargs) - _logger.debug('POST - %s - %s', url, response.text) + _logger.debug("POST - %s - %s", url, response.text) return self._read_response(response) def _put(self, url: str, data: Optional[dict] = None, **kwargs): response = self.client.put(url, json=data, **kwargs) - _logger.debug('PUT - %s - %s', url, response.text) + _logger.debug("PUT - %s - %s", url, response.text) return self._read_response(response) def _delete(self, url: str, **kwargs): response = self.client.delete(url, **kwargs) - _logger.debug('DELETE - %s - %s', url, response.text) + _logger.debug("DELETE - %s - %s", url, response.text) def __iter__(self) -> PaginateEntities: return self.list() @@ -120,16 +120,16 @@ def list(self, **kwargs) -> PaginateEntities: return self._list(self.entity_uri, **kwargs) def get_first(self, **kwargs) -> Optional[Entity]: - kwargs.setdefault('results-per-page', 1) + kwargs.setdefault("results-per-page", 1) for entity in self._list(self.entity_uri, **kwargs): return entity return None def get(self, entity_id: str, *extra_paths) -> Entity: if len(extra_paths) == 0: - requested_path = '%s/%s' % (self.entity_uri, entity_id) + requested_path = "%s/%s" % (self.entity_uri, entity_id) else: - requested_path = '%s/%s/%s' % (self.entity_uri, entity_id, '/'.join(extra_paths)) + requested_path = "%s/%s/%s" % (self.entity_uri, entity_id, "/".join(extra_paths)) return self._get(requested_path) def _read_response(self, response: Response, other_entity_builder: Optional[EntityBuilder] = None): @@ -148,25 +148,23 @@ def _get_entity_builder(self, entity_builder: Optional[EntityBuilder]) -> Entity return entity_builder def _get_url_filtered(self, url: str, **kwargs) -> str: - def _append_encoded_parameter(parameters: List[str], args: Tuple[str, Any]) -> List[str]: parameter_name, parameter_value = args[0], args[1] if parameter_name in self.list_query_parameters: - parameters.append('%s=%s' % (parameter_name, str(parameter_value))) + parameters.append("%s=%s" % (parameter_name, str(parameter_value))) elif parameter_name in self.list_multi_parameters: value_list = parameter_value if not isinstance(value_list, (list, tuple)): value_list = [value_list] for value in value_list: - parameters.append('%s=%s' % (parameter_name, str(value))) + parameters.append("%s=%s" % (parameter_name, str(value))) elif isinstance(parameter_value, (list, tuple)): - parameters.append('q=%s' % quote('%s IN %s' % (parameter_name, ','.join(parameter_value)))) + parameters.append("q=%s" % quote("%s IN %s" % (parameter_name, ",".join(parameter_value)))) else: - parameters.append('q=%s' % quote('%s:%s' % (parameter_name, str(parameter_value)))) + parameters.append("q=%s" % quote("%s:%s" % (parameter_name, str(parameter_value)))) return parameters if len(kwargs) > 0: - return '%s?%s' % (url, - "&".join(reduce(_append_encoded_parameter, sorted(list(kwargs.items())), []))) + return "%s?%s" % (url, "&".join(reduce(_append_encoded_parameter, sorted(list(kwargs.items())), []))) else: return url diff --git a/main/cloudfoundry_client/v2/events.py b/main/cloudfoundry_client/v2/events.py index b7b898e..9822165 100644 --- a/main/cloudfoundry_client/v2/events.py +++ b/main/cloudfoundry_client/v2/events.py @@ -1,11 +1,14 @@ -from typing import Generator +from typing import Generator, TYPE_CHECKING from cloudfoundry_client.v2.entities import EntityManager, Entity +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + class EventManager(EntityManager): - def __init__(self, target_endpoint: str, client: 'CloudFoundryClient'): - super(EventManager, self).__init__(target_endpoint, client, '/v2/events') + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(EventManager, self).__init__(target_endpoint, client, "/v2/events") def list_by_type(self, event_type: str) -> Generator[Entity, None, None]: return self._list(self.entity_uri, type=event_type) diff --git a/main/cloudfoundry_client/v2/jobs.py b/main/cloudfoundry_client/v2/jobs.py index 17f86fc..0d6f30f 100644 --- a/main/cloudfoundry_client/v2/jobs.py +++ b/main/cloudfoundry_client/v2/jobs.py @@ -1,10 +1,15 @@ +from typing import TYPE_CHECKING + from cloudfoundry_client.json_object import JsonObject +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + class JobManager(object): - def __init__(self, target_endpoint: str, client: 'CloudFoundryClient'): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): self.target_endpoint = target_endpoint self.client = client def get(self, job_guid: str) -> JsonObject: - return self.client.get('%s/v2/jobs/%s' % (self.target_endpoint, job_guid)).json(object_pairs_hook=JsonObject) + return self.client.get("%s/v2/jobs/%s" % (self.target_endpoint, job_guid)).json(object_pairs_hook=JsonObject) diff --git a/main/cloudfoundry_client/v2/resources.py b/main/cloudfoundry_client/v2/resources.py index e7fe88a..739c188 100644 --- a/main/cloudfoundry_client/v2/resources.py +++ b/main/cloudfoundry_client/v2/resources.py @@ -1,13 +1,16 @@ -from typing import List +from typing import List, TYPE_CHECKING from cloudfoundry_client.json_object import JsonObject +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + class ResourceManager(object): - def __init__(self, target_endpoint: str, client: 'CloudFoundryClient'): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): self.target_endpoint = target_endpoint self.client = client def match(self, items: List[dict]) -> List[JsonObject]: - response = self.client.put('%s/v2/resource_match' % self.client.info.api_endpoint, json=items) + response = self.client.put("%s/v2/resource_match" % self.client.info.api_endpoint, json=items) return response.json(object_pairs_hook=JsonObject) diff --git a/main/cloudfoundry_client/v2/routes.py b/main/cloudfoundry_client/v2/routes.py index 8a2702e..1e4d52c 100644 --- a/main/cloudfoundry_client/v2/routes.py +++ b/main/cloudfoundry_client/v2/routes.py @@ -1,20 +1,23 @@ -from typing import Optional +from typing import Optional, TYPE_CHECKING from cloudfoundry_client.v2.entities import EntityManager, Entity +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + class RouteManager(EntityManager): - def __init__(self, target_endpoint: str, client: 'CloudFoundryClient'): - super(RouteManager, self).__init__(target_endpoint, client, '/v2/routes') + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(RouteManager, self).__init__(target_endpoint, client, "/v2/routes") def create_tcp_route(self, domain_guid: str, space_guid: str, port: Optional[int] = None) -> Entity: request = self._request(domain_guid=domain_guid, space_guid=space_guid) if port is None: return super(RouteManager, self)._create(request, params=dict(generate_port=True)) else: - request['port'] = port + request["port"] = port return super(RouteManager, self)._create(request) - def create_host_route(self, domain_guid: str, space_guid: str, host: str, path: Optional[str] = '') -> Entity: + def create_host_route(self, domain_guid: str, space_guid: str, host: str, path: Optional[str] = "") -> Entity: request = dict(domain_guid=domain_guid, space_guid=space_guid, host=host, path=path) return super(RouteManager, self)._create(request) diff --git a/main/cloudfoundry_client/v2/service_bindings.py b/main/cloudfoundry_client/v2/service_bindings.py index 3d34865..00fc625 100644 --- a/main/cloudfoundry_client/v2/service_bindings.py +++ b/main/cloudfoundry_client/v2/service_bindings.py @@ -1,17 +1,19 @@ -from typing import Optional +from typing import Optional, TYPE_CHECKING from cloudfoundry_client.v2.entities import EntityManager, Entity +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + class ServiceBindingManager(EntityManager): - def __init__(self, target_endpoint: str, client: 'CloudFoundryClient'): - super(ServiceBindingManager, self).__init__(target_endpoint, client, '/v2/service_bindings') + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(ServiceBindingManager, self).__init__(target_endpoint, client, "/v2/service_bindings") - def create(self, app_guid: str, instance_guid: str, parameters: Optional[dict] = None, - name: Optional[str] = None) -> Entity: + def create(self, app_guid: str, instance_guid: str, parameters: Optional[dict] = None, name: Optional[str] = None) -> Entity: request = self._request(app_guid=app_guid, service_instance_guid=instance_guid) - request['parameters'] = parameters - request['name'] = name + request["parameters"] = parameters + request["name"] = name return super(ServiceBindingManager, self)._create(request) def remove(self, binding_id: str): diff --git a/main/cloudfoundry_client/v2/service_brokers.py b/main/cloudfoundry_client/v2/service_brokers.py index b57b798..abe44c7 100644 --- a/main/cloudfoundry_client/v2/service_brokers.py +++ b/main/cloudfoundry_client/v2/service_brokers.py @@ -1,26 +1,35 @@ -from typing import Optional +from typing import Optional, TYPE_CHECKING from cloudfoundry_client.v2.entities import EntityManager, Entity +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + class ServiceBrokerManager(EntityManager): - def __init__(self, target_endpoint: str, client: 'CloudFoundryClient'): - super(ServiceBrokerManager, self).__init__(target_endpoint, client, '/v2/service_brokers') + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(ServiceBrokerManager, self).__init__(target_endpoint, client, "/v2/service_brokers") - def create(self, broker_url: str, broker_name: str, auth_username: str, auth_password: str, - space_guid: Optional[str] = None) -> Entity: - request = self._request(broker_url=broker_url, name=broker_name, - auth_username=auth_username, auth_password=auth_password) - request['space_guid'] = space_guid + def create( + self, broker_url: str, broker_name: str, auth_username: str, auth_password: str, space_guid: Optional[str] = None + ) -> Entity: + request = self._request(broker_url=broker_url, name=broker_name, auth_username=auth_username, auth_password=auth_password) + request["space_guid"] = space_guid return super(ServiceBrokerManager, self)._create(request) - def update(self, broker_guid: str, broker_url: Optional[str] = None, broker_name: Optional[str] = None, - auth_username: Optional[str] = None, auth_password: Optional[str] = None) -> Entity: + def update( + self, + broker_guid: str, + broker_url: Optional[str] = None, + broker_name: Optional[str] = None, + auth_username: Optional[str] = None, + auth_password: Optional[str] = None, + ) -> Entity: request = self._request() - request['broker_url'] = broker_url - request['name'] = broker_name - request['auth_username'] = auth_username - request['auth_password'] = auth_password + request["broker_url"] = broker_url + request["name"] = broker_name + request["auth_username"] = auth_username + request["auth_password"] = auth_password return super(ServiceBrokerManager, self)._update(broker_guid, request) def remove(self, broker_guid): diff --git a/main/cloudfoundry_client/v2/service_instances.py b/main/cloudfoundry_client/v2/service_instances.py index 6e67f0a..f17fa7f 100644 --- a/main/cloudfoundry_client/v2/service_instances.py +++ b/main/cloudfoundry_client/v2/service_instances.py @@ -1,42 +1,56 @@ -from typing import Optional, List, Dict +from typing import Optional, List, Dict, TYPE_CHECKING from cloudfoundry_client.v2.entities import EntityManager, Entity +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient -class ServiceInstanceManager(EntityManager): - list_query_parameters = ['page', 'results-per-page', 'order-direction', 'return_user_provided_service_instances'] - - def __init__(self, target_endpoint: str, client: 'CloudFoundryClient'): - super(ServiceInstanceManager, self).__init__(target_endpoint, client, '/v2/service_instances') - def create(self, space_guid: str, instance_name: str, plan_guid: str, - parameters: Optional[dict] = None, tags: List[str] = None, - accepts_incomplete: Optional[bool] = False) -> Entity: +class ServiceInstanceManager(EntityManager): + list_query_parameters = ["page", "results-per-page", "order-direction", "return_user_provided_service_instances"] + + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(ServiceInstanceManager, self).__init__(target_endpoint, client, "/v2/service_instances") + + def create( + self, + space_guid: str, + instance_name: str, + plan_guid: str, + parameters: Optional[dict] = None, + tags: List[str] = None, + accepts_incomplete: Optional[bool] = False, + ) -> Entity: request = self._request(name=instance_name, space_guid=space_guid, service_plan_guid=plan_guid) - request['parameters'] = parameters - request['tags'] = tags + request["parameters"] = parameters + request["tags"] = tags params = None if not accepts_incomplete else dict(accepts_incomplete="true") return super(ServiceInstanceManager, self)._create(request, params=params) - def update(self, instance_guid: str, instance_name: Optional[str] = None, plan_guid: Optional[str] = None, - parameters: Optional[dict] = None, tags: List[str] = None, - accepts_incomplete: Optional[bool] = False) -> Entity: + def update( + self, + instance_guid: str, + instance_name: Optional[str] = None, + plan_guid: Optional[str] = None, + parameters: Optional[dict] = None, + tags: List[str] = None, + accepts_incomplete: Optional[bool] = False, + ) -> Entity: request = self._request() - request['name'] = instance_name - request['service_plan_guid'] = plan_guid - request['parameters'] = parameters - request['tags'] = tags + request["name"] = instance_name + request["service_plan_guid"] = plan_guid + request["parameters"] = parameters + request["tags"] = tags params = None if not accepts_incomplete else dict(accepts_incomplete="true") return super(ServiceInstanceManager, self)._update(instance_guid, request, params=params) def list_permissions(self, instance_guid: str) -> Dict[str, bool]: - return super(ServiceInstanceManager, self)._get('%s/%s/permissions' % (self.entity_uri, instance_guid), - dict) + return super(ServiceInstanceManager, self)._get("%s/%s/permissions" % (self.entity_uri, instance_guid), dict) def remove(self, instance_guid: str, accepts_incomplete: Optional[bool] = False, purge: Optional[bool] = False): parameters = {} if accepts_incomplete: - parameters['accepts_incomplete'] = "true" + parameters["accepts_incomplete"] = "true" if purge: - parameters['purge'] = "true" + parameters["purge"] = "true" super(ServiceInstanceManager, self)._remove(instance_guid, params=parameters) diff --git a/main/cloudfoundry_client/v2/service_keys.py b/main/cloudfoundry_client/v2/service_keys.py index 287268c..cb87c3e 100644 --- a/main/cloudfoundry_client/v2/service_keys.py +++ b/main/cloudfoundry_client/v2/service_keys.py @@ -1,15 +1,18 @@ -from typing import Optional +from typing import Optional, TYPE_CHECKING from cloudfoundry_client.v2.entities import EntityManager, Entity +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + class ServiceKeyManager(EntityManager): - def __init__(self, target_endpoint: str, client: 'CloudfoundryClient'): - super(ServiceKeyManager, self).__init__(target_endpoint, client, '/v2/service_keys') + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(ServiceKeyManager, self).__init__(target_endpoint, client, "/v2/service_keys") def create(self, service_instance_guid: str, name: str, parameters: Optional[dict] = None) -> Entity: request = self._request(service_instance_guid=service_instance_guid, name=name) - request['parameters'] = parameters + request["parameters"] = parameters return super(ServiceKeyManager, self)._create(request) def remove(self, key_guid: str): diff --git a/main/cloudfoundry_client/v2/service_plan_visibilities.py b/main/cloudfoundry_client/v2/service_plan_visibilities.py index 15bb285..4d7d513 100644 --- a/main/cloudfoundry_client/v2/service_plan_visibilities.py +++ b/main/cloudfoundry_client/v2/service_plan_visibilities.py @@ -1,20 +1,25 @@ +from typing import TYPE_CHECKING + from cloudfoundry_client.v2.entities import EntityManager, Entity +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + class ServicePlanVisibilityManager(EntityManager): - def __init__(self, target_endpoint: str, client: 'CloudFoundryClient'): - super(ServicePlanVisibilityManager, self).__init__(target_endpoint, client, '/v2/service_plan_visibilities') + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(ServicePlanVisibilityManager, self).__init__(target_endpoint, client, "/v2/service_plan_visibilities") def create(self, service_plan_guid: str, organization_guid: str) -> Entity: request = self._request() - request['service_plan_guid'] = service_plan_guid - request['organization_guid'] = organization_guid + request["service_plan_guid"] = service_plan_guid + request["organization_guid"] = organization_guid return super(ServicePlanVisibilityManager, self)._create(request) def update(self, spv_guid: str, service_plan_guid: str, organization_guid: str) -> Entity: request = self._request() - request['service_plan_guid'] = service_plan_guid - request['organization_guid'] = organization_guid + request["service_plan_guid"] = service_plan_guid + request["organization_guid"] = organization_guid return super(ServicePlanVisibilityManager, self)._update(spv_guid, request) def remove(self, spv_guid: str): diff --git a/main/cloudfoundry_client/v2/service_plans.py b/main/cloudfoundry_client/v2/service_plans.py index 50c0a15..487b1cd 100644 --- a/main/cloudfoundry_client/v2/service_plans.py +++ b/main/cloudfoundry_client/v2/service_plans.py @@ -1,13 +1,17 @@ +from typing import TYPE_CHECKING + from cloudfoundry_client.v2.entities import EntityManager, Entity, PaginateEntities +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + class ServicePlanManager(EntityManager): - def __init__(self, target_endpoint: str, client: 'CloudFoundryClient'): - super(ServicePlanManager, self).__init__(target_endpoint, client, '/v2/service_plans') + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(ServicePlanManager, self).__init__(target_endpoint, client, "/v2/service_plans") def create_from_resource_file(self, path: str) -> Entity: - raise NotImplementedError('No creation allowed') + raise NotImplementedError("No creation allowed") def list_instances(self, service_plan_guid: str, **kwargs) -> PaginateEntities: - return self.client.v2.service_instances._list('%s/%s/service_instances' % (self.entity_uri, service_plan_guid), - **kwargs) + return self.client.v2.service_instances._list("%s/%s/service_instances" % (self.entity_uri, service_plan_guid), **kwargs) diff --git a/main/cloudfoundry_client/v3/apps.py b/main/cloudfoundry_client/v3/apps.py index 82daa5e..ca041f5 100644 --- a/main/cloudfoundry_client/v3/apps.py +++ b/main/cloudfoundry_client/v3/apps.py @@ -1,16 +1,21 @@ +from typing import TYPE_CHECKING + from cloudfoundry_client.json_object import JsonObject from cloudfoundry_client.v3.entities import EntityManager +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + class AppManager(EntityManager): - def __init__(self, target_endpoint: str, client: 'CloudfoundryClient'): - super(AppManager, self).__init__(target_endpoint, client, '/v3/apps') + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(AppManager, self).__init__(target_endpoint, client, "/v3/apps") def remove(self, application_guid: str): super(AppManager, self)._remove(application_guid) def get_env(self, application_guid: str) -> JsonObject: - return super(AppManager, self)._get('%s%s/%s/env' % (self.target_endpoint, self.entity_uri, application_guid)) + return super(AppManager, self)._get("%s%s/%s/env" % (self.target_endpoint, self.entity_uri, application_guid)) def get_routes(self, application_guid: str) -> JsonObject: - return super(AppManager, self)._get('%s%s/%s/routes' % (self.target_endpoint, self.entity_uri, application_guid)) + return super(AppManager, self)._get("%s%s/%s/routes" % (self.target_endpoint, self.entity_uri, application_guid)) diff --git a/main/cloudfoundry_client/v3/buildpacks.py b/main/cloudfoundry_client/v3/buildpacks.py index 351d166..f89f043 100644 --- a/main/cloudfoundry_client/v3/buildpacks.py +++ b/main/cloudfoundry_client/v3/buildpacks.py @@ -1,46 +1,56 @@ -from typing import Optional +from typing import Optional, TYPE_CHECKING from cloudfoundry_client.v3.entities import EntityManager, Entity +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + class BuildpackManager(EntityManager): - def __init__(self, target_endpoint: str, client: 'CloudfoundryClient'): - super(BuildpackManager, self).__init__(target_endpoint, client, '/v3/buildpacks') + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(BuildpackManager, self).__init__(target_endpoint, client, "/v3/buildpacks") - def create(self, name: str, position: Optional[int] = 0, - enabled: Optional[bool] = True, locked: Optional[bool] = False, - stack: Optional[str] = None, - meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Entity: + def create( + self, + name: str, + position: Optional[int] = 0, + enabled: Optional[bool] = True, + locked: Optional[bool] = False, + stack: Optional[str] = None, + meta_labels: Optional[dict] = None, + meta_annotations: Optional[dict] = None, + ) -> Entity: data = { - 'name': name, - 'position': position, - 'enabled': enabled, - 'locked': locked, - 'stack': stack, - 'metadata': { - 'labels': meta_labels, - 'annotations': meta_annotations - } + "name": name, + "position": position, + "enabled": enabled, + "locked": locked, + "stack": stack, + "metadata": {"labels": meta_labels, "annotations": meta_annotations}, } return super(BuildpackManager, self)._create(data) def remove(self, buildpack_guid: str): super(BuildpackManager, self)._remove(buildpack_guid) - def update(self, buildpack_guid: str, name: str, position: Optional[int] = 0, - enabled: Optional[bool] = True, locked: Optional[bool] = False, - stack: Optional[str] = None, - meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Entity: + def update( + self, + buildpack_guid: str, + name: str, + position: Optional[int] = 0, + enabled: Optional[bool] = True, + locked: Optional[bool] = False, + stack: Optional[str] = None, + meta_labels: Optional[dict] = None, + meta_annotations: Optional[dict] = None, + ) -> Entity: data = { - 'name': name, - 'position': position, - 'enabled': enabled, - 'locked': locked, - 'stack': stack, - 'metadata': { - 'labels': meta_labels, - 'annotations': meta_annotations - } + "name": name, + "position": position, + "enabled": enabled, + "locked": locked, + "stack": stack, + "metadata": {"labels": meta_labels, "annotations": meta_annotations}, } return super(BuildpackManager, self)._update(buildpack_guid, data) diff --git a/main/cloudfoundry_client/v3/domains.py b/main/cloudfoundry_client/v3/domains.py index 3716dec..084a7cf 100644 --- a/main/cloudfoundry_client/v3/domains.py +++ b/main/cloudfoundry_client/v3/domains.py @@ -1,52 +1,51 @@ -from typing import Optional +from typing import Optional, TYPE_CHECKING -from cloudfoundry_client.v3.entities import EntityManager, ToOneRelationship, ToManyRelationship, PaginateEntities, \ - Entity +from cloudfoundry_client.v3.entities import EntityManager, ToOneRelationship, ToManyRelationship, PaginateEntities, Entity + +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient class Domain(Entity): - def __init__(self, target_endpoint: str, entity_manager: 'EntityManager', **kwargs): + def __init__(self, target_endpoint: str, entity_manager: "EntityManager", **kwargs): super(Domain, self).__init__(target_endpoint, entity_manager, **kwargs) - relationships = self['relationships'] - if 'organization' in relationships: - self['relationships']['organization'] = ToOneRelationship.from_json_object(relationships['organization']) - if 'shared_organizations' in relationships: - self['relationships']['shared_organizations'] \ - = ToManyRelationship.from_json_object(relationships['shared_organizations']) + relationships = self["relationships"] + if "organization" in relationships: + self["relationships"]["organization"] = ToOneRelationship.from_json_object(relationships["organization"]) + if "shared_organizations" in relationships: + self["relationships"]["shared_organizations"] = ToManyRelationship.from_json_object( + relationships["shared_organizations"] + ) class DomainManager(EntityManager): - def __init__(self, target_endpoint: str, client: 'CloudfoundryClient'): - super(DomainManager, self).__init__(target_endpoint, client, '/v3/domains', Domain) + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(DomainManager, self).__init__(target_endpoint, client, "/v3/domains", Domain) - def create(self, name: str, internal: Optional[bool] = False, - organization: Optional[ToOneRelationship] = None, - shared_organizations: Optional[ToManyRelationship] = None, - meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Domain: + def create( + self, + name: str, + internal: Optional[bool] = False, + organization: Optional[ToOneRelationship] = None, + shared_organizations: Optional[ToManyRelationship] = None, + meta_labels: Optional[dict] = None, + meta_annotations: Optional[dict] = None, + ) -> Domain: data = { - 'name': name, - 'internal': internal, - 'organization': organization, - 'shared_organizations': shared_organizations, - 'metadata': { - 'labels': meta_labels, - 'annotations': meta_annotations - } + "name": name, + "internal": internal, + "organization": organization, + "shared_organizations": shared_organizations, + "metadata": {"labels": meta_labels, "annotations": meta_annotations}, } return super(DomainManager, self)._create(data) def list_domains_for_org(self, org_guid: str, **kwargs) -> PaginateEntities: - uri = '/v3/organizations/{guid}/domains'.format(guid=org_guid) + uri = "/v3/organizations/{guid}/domains".format(guid=org_guid) return self._list(uri, **kwargs) - def update(self, domain_guid: str, - meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Domain: - data = { - 'metadata': { - 'labels': meta_labels, - 'annotations': meta_annotations - } - } + def update(self, domain_guid: str, meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Domain: + data = {"metadata": {"labels": meta_labels, "annotations": meta_annotations}} return super(DomainManager, self)._update(domain_guid, data) def remove(self, domain_guid: str): @@ -54,15 +53,14 @@ def remove(self, domain_guid: str): def __create_shared_domain_url(self, domain_guid: str) -> str: # TODO use url parser for this - return '{endpoint}{entity}/{domain}/relationships/shared_organizations' \ - ''.format(endpoint=self.target_endpoint, entity=self.entity_uri, - domain=domain_guid) + return "{endpoint}{entity}/{domain}/relationships/shared_organizations" "".format( + endpoint=self.target_endpoint, entity=self.entity_uri, domain=domain_guid + ) def share_domain(self, domain_guid: str, organization_guids: ToManyRelationship) -> ToManyRelationship: url = self.__create_shared_domain_url(domain_guid) return ToManyRelationship.from_json_object(super(DomainManager, self)._post(url, data=organization_guids)) def unshare_domain(self, domain_guid: str, org_guid: str): - url = '{uri}/{org}'.format(uri=self.__create_shared_domain_url(domain_guid), - org=org_guid) + url = "{uri}/{org}".format(uri=self.__create_shared_domain_url(domain_guid), org=org_guid) super(DomainManager, self)._delete(url) diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index 7f8ef13..ea5502a 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -1,6 +1,6 @@ import functools import logging -from typing import Any, Generator, Optional, List, Tuple, Union, TypeVar +from typing import Any, Generator, Optional, List, Tuple, Union, TypeVar, TYPE_CHECKING from urllib.parse import quote from requests import Response @@ -9,37 +9,41 @@ from cloudfoundry_client.json_object import JsonObject from cloudfoundry_client.request_object import Request +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + _logger = logging.getLogger(__name__) class Entity(JsonObject): - def __init__(self, target_endpoint: str, client: 'CloudFoundryClient', **kwargs): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient", **kwargs): super(Entity, self).__init__(**kwargs) try: + def default_method(m, u): - raise NotImplementedError('Unknown method %s for url %s' % (m, u)) + raise NotImplementedError("Unknown method %s for url %s" % (m, u)) - for link_name, link in self.get('links', {}).items(): - if link_name != 'self': - link_method = link.get('method', 'GET').lower() - ref = link['href'] - manager_name = link_name if link_name.endswith('s') else '%ss' % link_name + for link_name, link in self.get("links", {}).items(): + if link_name != "self": + link_method = link.get("method", "GET").lower() + ref = link["href"] + manager_name = link_name if link_name.endswith("s") else "%ss" % link_name try: other_manager = getattr(client.v3, manager_name) except AttributeError: # generic manager - other_manager = EntityManager( - target_endpoint, - client, - '') - if link_method == 'get': - new_method = functools.partial(other_manager._paginate, ref) if link_name.endswith('s') \ + other_manager = EntityManager(target_endpoint, client, "") + if link_method == "get": + new_method = ( + functools.partial(other_manager._paginate, ref) + if link_name.endswith("s") else functools.partial(other_manager._get, ref) - elif link_method == 'post': + ) + elif link_method == "post": new_method = functools.partial(other_manager._post, ref) - elif link_method == 'put': + elif link_method == "put": new_method = functools.partial(other_manager._put, ref) - elif link_method == 'delete': + elif link_method == "delete": new_method = functools.partial(other_manager._delete, ref) else: new_method = functools.partial(default_method, link_method, ref) @@ -62,8 +66,8 @@ class ToOneRelationship(JsonObject): def from_json_object(to_one_relationship: JsonObject): if to_one_relationship is None: return ToOneRelationship(None) - data = to_one_relationship.pop('data', None) - result = ToOneRelationship(None if data is None else data['guid']) + data = to_one_relationship.pop("data", None) + result = ToOneRelationship(None if data is None else data["guid"]) result.update(to_one_relationship) return result @@ -75,7 +79,7 @@ def __init__(self, guid: Optional[str]): class ToManyRelationship(JsonObject): @staticmethod def from_json_object(to_many_relations: JsonObject): - result = ToManyRelationship(*[relation['guid'] for relation in to_many_relations.pop('data')]) + result = ToManyRelationship(*[relation["guid"] for relation in to_many_relations.pop("data")]) result.update(to_many_relations) return result @@ -84,77 +88,77 @@ def __init__(self, *guids: str): self.guids = list(guids) -ENTITY_TYPE = TypeVar('ENTITY_TYPE', bound=Entity) +ENTITY_TYPE = TypeVar("ENTITY_TYPE", bound=Entity) class EntityManager(object): - def __init__(self, target_endpoint: str, client: 'CloudFoundryClient', entity_uri: str, - entity_type: ENTITY_TYPE = Entity): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient", entity_uri: str, entity_type: ENTITY_TYPE = Entity): self.target_endpoint = target_endpoint self.entity_uri = entity_uri self.client = client self.entity_type = entity_type - def _post(self, url: str, data: Optional[dict] = None, files: Any = None, - entity_type: ENTITY_TYPE = None) -> Entity: + def _post(self, url: str, data: Optional[dict] = None, files: Any = None, entity_type: ENTITY_TYPE = None) -> Entity: response = self.client.post(url, json=data, files=files) - _logger.debug('POST - %s - %s', url, response.text) + _logger.debug("POST - %s - %s", url, response.text) return self._read_response(response, entity_type) def _get(self, url: str, entity_type: Optional[ENTITY_TYPE] = None) -> Entity: response = self.client.get(url) - _logger.debug('GET - %s - %s', url, response.text) + _logger.debug("GET - %s - %s", url, response.text) return self._read_response(response, entity_type) def _put(self, url: str, data: dict, entity_type: Optional[ENTITY_TYPE] = None) -> Entity: response = self.client.put(url, json=data) - _logger.debug('PUT - %s - %s', url, response.text) + _logger.debug("PUT - %s - %s", url, response.text) return self._read_response(response, entity_type) def _patch(self, url: str, data: dict, entity_type: Optional[ENTITY_TYPE] = None) -> Entity: response = self.client.patch(url, json=data) - _logger.debug('PATCH - %s - %s', url, response.text) + _logger.debug("PATCH - %s - %s", url, response.text) return self._read_response(response, entity_type) def _delete(self, url: str): response = self.client.delete(url) - _logger.debug('DELETE - %s - %s', url, response.text) + _logger.debug("DELETE - %s - %s", url, response.text) def _list(self, requested_path: str, entity_type: Optional[ENTITY_TYPE] = None, **kwargs) -> PaginateEntities: - url_requested = EntityManager._get_url_filtered('%s%s' % (self.target_endpoint, requested_path), **kwargs) + url_requested = EntityManager._get_url_filtered("%s%s" % (self.target_endpoint, requested_path), **kwargs) for element in self._paginate(url_requested, entity_type): yield element def _paginate(self, url_requested: str, entity_type: Optional[ENTITY_TYPE] = None) -> PaginateEntities: response = self.client.get(url_requested) while True: - _logger.debug('GET - %s - %s', url_requested, response.text) + _logger.debug("GET - %s - %s", url_requested, response.text) response_json = self._read_response(response, JsonObject) - for resource in response_json['resources']: + for resource in response_json["resources"]: yield self._entity(resource, entity_type) - if 'next' not in response_json['pagination'] \ - or response_json['pagination']['next'] is None \ - or response_json['pagination']['next'].get('href') is None: + if ( + "next" not in response_json["pagination"] + or response_json["pagination"]["next"] is None + or response_json["pagination"]["next"].get("href") is None + ): break else: - url_requested = response_json['pagination']['next']['href'] + url_requested = response_json["pagination"]["next"]["href"] response = self.client.get(url_requested) def _create(self, data: dict) -> Entity: - url = '%s%s' % (self.target_endpoint, self.entity_uri) + url = "%s%s" % (self.target_endpoint, self.entity_uri) return self._post(url, data=data) def _upload_bits(self, resource_id: str, filename: str) -> Entity: - url = '%s%s/%s/upload' % (self.target_endpoint, self.entity_uri, resource_id) - files = {'bits': (filename, open(filename, 'rb'))} + url = "%s%s/%s/upload" % (self.target_endpoint, self.entity_uri, resource_id) + files = {"bits": (filename, open(filename, "rb"))} return self._post(url, files=files) def _update(self, resource_id: str, data: dict) -> Entity: - url = '%s%s/%s' % (self.target_endpoint, self.entity_uri, resource_id) + url = "%s%s/%s" % (self.target_endpoint, self.entity_uri, resource_id) return self._patch(url, data) def _remove(self, resource_id: str): - url = '%s%s/%s' % (self.target_endpoint, self.entity_uri, resource_id) + url = "%s%s/%s" % (self.target_endpoint, self.entity_uri, resource_id) self._delete(url) def __iter__(self) -> PaginateEntities: @@ -167,16 +171,16 @@ def list(self, **kwargs) -> PaginateEntities: return self._list(self.entity_uri, **kwargs) def get_first(self, **kwargs) -> Optional[Entity]: - kwargs.setdefault('per_page', 1) + kwargs.setdefault("per_page", 1) for entity in self._list(self.entity_uri, **kwargs): return entity return None def get(self, entity_id: str, *extra_paths) -> Entity: if len(extra_paths) == 0: - requested_path = '%s%s/%s' % (self.target_endpoint, self.entity_uri, entity_id) + requested_path = "%s%s/%s" % (self.target_endpoint, self.entity_uri, entity_id) else: - requested_path = '%s%s/%s/%s' % (self.target_endpoint, self.entity_uri, entity_id, '/'.join(extra_paths)) + requested_path = "%s%s/%s/%s" % (self.target_endpoint, self.entity_uri, entity_id, "/".join(extra_paths)) return self._get(requested_path) def _read_response(self, response: Response, entity_type: Optional[ENTITY_TYPE]) -> Union[JsonObject, Entity]: @@ -188,7 +192,7 @@ def _request(**mandatory_parameters) -> Request: return Request(**mandatory_parameters) def _entity(self, result: JsonObject, entity_type: Optional[ENTITY_TYPE]) -> Union[JsonObject, Entity]: - if 'guid' in result: + if "guid" in result: return (entity_type or self.entity_type)(self.target_endpoint, self.client, **result) else: return result @@ -198,13 +202,12 @@ def _get_url_filtered(url: str, **kwargs) -> str: def _append_encoded_parameter(parameters: List[str], args: Tuple[str, Any]) -> List[str]: parameter_name, parameter_value = args[0], args[1] if isinstance(parameter_value, (list, tuple)): - parameters.append('%s=%s' % (parameter_name, quote(','.join(parameter_value)))) + parameters.append("%s=%s" % (parameter_name, quote(",".join(parameter_value)))) else: - parameters.append('%s=%s' % (parameter_name, quote(str(parameter_value)))) + parameters.append("%s=%s" % (parameter_name, quote(str(parameter_value)))) return parameters if len(kwargs) > 0: - return '%s?%s' % (url, - "&".join(functools.reduce(_append_encoded_parameter, sorted(list(kwargs.items())), []))) + return "%s?%s" % (url, "&".join(functools.reduce(_append_encoded_parameter, sorted(list(kwargs.items())), []))) else: return url diff --git a/main/cloudfoundry_client/v3/feature_flags.py b/main/cloudfoundry_client/v3/feature_flags.py index 440a885..47c35d8 100644 --- a/main/cloudfoundry_client/v3/feature_flags.py +++ b/main/cloudfoundry_client/v3/feature_flags.py @@ -1,15 +1,15 @@ -from typing import Optional +from typing import Optional, TYPE_CHECKING from cloudfoundry_client.v3.entities import EntityManager, Entity +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + class FeatureFlagManager(EntityManager): - def __init__(self, target_endpoint: str, client: 'CloudfoundryClient'): - super(FeatureFlagManager, self).__init__(target_endpoint, client, '/v3/feature_flags') + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(FeatureFlagManager, self).__init__(target_endpoint, client, "/v3/feature_flags") def update(self, name: str, enabled: Optional[bool] = True, custom_error_message: Optional[str] = None) -> Entity: - data = { - 'enabled': enabled, - 'custom_error_message': custom_error_message - } + data = {"enabled": enabled, "custom_error_message": custom_error_message} return super(FeatureFlagManager, self)._update(name, data) diff --git a/main/cloudfoundry_client/v3/isolation_segments.py b/main/cloudfoundry_client/v3/isolation_segments.py index 4769f6c..67fa114 100644 --- a/main/cloudfoundry_client/v3/isolation_segments.py +++ b/main/cloudfoundry_client/v3/isolation_segments.py @@ -1,53 +1,57 @@ -from typing import Optional +from typing import Optional, TYPE_CHECKING from cloudfoundry_client.v3.entities import EntityManager, Entity, ToManyRelationship +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + class IsolationSegmentManager(EntityManager): - def __init__(self, target_endpoint: str, client: 'CloudfoundryClient'): - super(IsolationSegmentManager, self).__init__(target_endpoint, client, '/v3/isolation_segments') - - def create(self, name: str, - meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Entity: - data = { - 'name': name, - 'metadata': { - 'labels': meta_labels, - 'annotations': meta_annotations - } - } + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(IsolationSegmentManager, self).__init__(target_endpoint, client, "/v3/isolation_segments") + + def create(self, name: str, meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Entity: + data = {"name": name, "metadata": {"labels": meta_labels, "annotations": meta_annotations}} return super(IsolationSegmentManager, self)._create(data) - def update(self, isolation_segment_guid: str, name: str, - meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Entity: - data = { - 'name': name, - 'metadata': { - 'labels': meta_labels, - 'annotations': meta_annotations - } - } + def update( + self, isolation_segment_guid: str, name: str, meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None + ) -> Entity: + data = {"name": name, "metadata": {"labels": meta_labels, "annotations": meta_annotations}} return super(IsolationSegmentManager, self)._update(isolation_segment_guid, data) def entitle_organizations(self, isolation_segment_guid: str, *org_guids: str) -> ToManyRelationship: data = ToManyRelationship(*org_guids) - return ToManyRelationship.from_json_object(super(IsolationSegmentManager, self)._post( - '%s%s/%s/relationships/organizations' % (self.target_endpoint, self.entity_uri, isolation_segment_guid), - data=data)) - - def list_entitled_organizations(self, isolation_segment_guid: str, ) -> ToManyRelationship: - return ToManyRelationship.from_json_object(super(IsolationSegmentManager, self)._get( - '%s%s/%s/relationships/organizations' % (self.target_endpoint, self.entity_uri, isolation_segment_guid))) - - def list_entitled_spaces(self, isolation_segment_guid: str, ) -> ToManyRelationship: - return ToManyRelationship.from_json_object(super(IsolationSegmentManager, self)._get( - '%s%s/%s/relationships/spaces' % (self.target_endpoint, self.entity_uri, isolation_segment_guid))) + return ToManyRelationship.from_json_object( + super(IsolationSegmentManager, self)._post( + "%s%s/%s/relationships/organizations" % (self.target_endpoint, self.entity_uri, isolation_segment_guid), data=data + ) + ) + + def list_entitled_organizations( + self, + isolation_segment_guid: str, + ) -> ToManyRelationship: + return ToManyRelationship.from_json_object( + super(IsolationSegmentManager, self)._get( + "%s%s/%s/relationships/organizations" % (self.target_endpoint, self.entity_uri, isolation_segment_guid) + ) + ) + + def list_entitled_spaces( + self, + isolation_segment_guid: str, + ) -> ToManyRelationship: + return ToManyRelationship.from_json_object( + super(IsolationSegmentManager, self)._get( + "%s%s/%s/relationships/spaces" % (self.target_endpoint, self.entity_uri, isolation_segment_guid) + ) + ) def revoke_organization(self, isolation_segment_guid: str, org_guid: str): - super(IsolationSegmentManager, self)._delete('%s%s/%s/relationships/organizations/%s' % (self.target_endpoint, - self.entity_uri, - isolation_segment_guid, - org_guid)) + super(IsolationSegmentManager, self)._delete( + "%s%s/%s/relationships/organizations/%s" % (self.target_endpoint, self.entity_uri, isolation_segment_guid, org_guid) + ) def remove(self, isolation_segment_guid: str): super(IsolationSegmentManager, self)._remove(isolation_segment_guid) diff --git a/main/cloudfoundry_client/v3/organizations.py b/main/cloudfoundry_client/v3/organizations.py index 93061ec..601cd83 100644 --- a/main/cloudfoundry_client/v3/organizations.py +++ b/main/cloudfoundry_client/v3/organizations.py @@ -1,36 +1,30 @@ -from typing import Optional +from typing import Optional, TYPE_CHECKING from cloudfoundry_client.v3.entities import EntityManager, Entity, ToOneRelationship +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + class OrganizationManager(EntityManager): - def __init__(self, target_endpoint: str, client: 'CloudfoundryClient'): - super(OrganizationManager, self).__init__(target_endpoint, client, '/v3/organizations') - - def create(self, name: str, suspended: bool, - meta_labels: Optional[dict] = None, - meta_annotations: Optional[dict] = None) -> Entity: - data = { - 'name': name, - 'suspended': suspended, - 'metadata': { - 'labels': meta_labels, - 'annotations': meta_annotations - } - } + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(OrganizationManager, self).__init__(target_endpoint, client, "/v3/organizations") + + def create( + self, name: str, suspended: bool, meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None + ) -> Entity: + data = {"name": name, "suspended": suspended, "metadata": {"labels": meta_labels, "annotations": meta_annotations}} return super(OrganizationManager, self)._create(data) - def update(self, guid: str, name: str, suspended: Optional[bool], - meta_labels: Optional[dict] = None, - meta_annotations: Optional[dict] = None) -> Entity: - data = { - 'name': name, - 'suspended': suspended, - 'metadata': { - 'labels': meta_labels, - 'annotations': meta_annotations - } - } + def update( + self, + guid: str, + name: str, + suspended: Optional[bool], + meta_labels: Optional[dict] = None, + meta_annotations: Optional[dict] = None, + ) -> Entity: + data = {"name": name, "suspended": suspended, "metadata": {"labels": meta_labels, "annotations": meta_annotations}} return super(OrganizationManager, self)._update(guid, data) def remove(self, guid: str): @@ -39,15 +33,18 @@ def remove(self, guid: str): def assign_default_isolation_segment(self, org_guid: str, iso_seg_guid: str) -> Entity: return ToOneRelationship.from_json_object( super(OrganizationManager, self)._patch( - '%s%s/%s/relationships/default_isolation_segment' % (self.target_endpoint, self.entity_uri, org_guid), - data=ToOneRelationship(iso_seg_guid))) + "%s%s/%s/relationships/default_isolation_segment" % (self.target_endpoint, self.entity_uri, org_guid), + data=ToOneRelationship(iso_seg_guid), + ) + ) def get_default_isolation_segment(self, guid: str) -> ToOneRelationship: return ToOneRelationship.from_json_object( - super(OrganizationManager, self).get(guid, 'relationships', 'default_isolation_segment')) + super(OrganizationManager, self).get(guid, "relationships", "default_isolation_segment") + ) def get_default_domain(self, guid: str) -> Entity: - return super(OrganizationManager, self).get(guid, 'domains', 'default') + return super(OrganizationManager, self).get(guid, "domains", "default") def get_usage_summary(self, guid: str) -> Entity: - return super(OrganizationManager, self).get(guid, 'usage_summary') + return super(OrganizationManager, self).get(guid, "usage_summary") diff --git a/main/cloudfoundry_client/v3/spaces.py b/main/cloudfoundry_client/v3/spaces.py index 1ca7267..f871a43 100644 --- a/main/cloudfoundry_client/v3/spaces.py +++ b/main/cloudfoundry_client/v3/spaces.py @@ -1,28 +1,35 @@ -from typing import Optional +from typing import Optional, TYPE_CHECKING from cloudfoundry_client.v3.entities import EntityManager, ToOneRelationship, Entity +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + class SpaceManager(EntityManager): - def __init__(self, target_endpoint: str, client: 'CloudfoundryClient'): - super(SpaceManager, self).__init__(target_endpoint, client, '/v3/spaces') + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(SpaceManager, self).__init__(target_endpoint, client, "/v3/spaces") def create(self, name: str, org_guid: str) -> Entity: - return super(SpaceManager, self)._create(dict(name=name, - relationships=dict(organization=ToOneRelationship(org_guid)))) + return super(SpaceManager, self)._create(dict(name=name, relationships=dict(organization=ToOneRelationship(org_guid)))) def update(self, space_guid: str, name: str) -> Entity: return super(SpaceManager, self)._update(space_guid, dict(name=name)) def get_assigned_isolation_segment(self, space_guid: str) -> ToOneRelationship: - return ToOneRelationship.from_json_object(super(SpaceManager, self)._get( - '%s%s/%s/relationships/isolation_segment' % (self.target_endpoint, self.entity_uri, space_guid))) + return ToOneRelationship.from_json_object( + super(SpaceManager, self)._get( + "%s%s/%s/relationships/isolation_segment" % (self.target_endpoint, self.entity_uri, space_guid) + ) + ) def assign_isolation_segment(self, space_guid: str, isolation_segment_guid: Optional[str]) -> ToOneRelationship: - return ToOneRelationship.from_json_object(super(SpaceManager, self)._patch( - '%s%s/%s/relationships/isolation_segment' % (self.target_endpoint, self.entity_uri, space_guid), - dict(data=None) if isolation_segment_guid is None else ToOneRelationship(isolation_segment_guid) - )) + return ToOneRelationship.from_json_object( + super(SpaceManager, self)._patch( + "%s%s/%s/relationships/isolation_segment" % (self.target_endpoint, self.entity_uri, space_guid), + dict(data=None) if isolation_segment_guid is None else ToOneRelationship(isolation_segment_guid), + ) + ) def remove(self, space_guid: str): super(SpaceManager, self)._remove(space_guid) diff --git a/main/cloudfoundry_client/v3/tasks.py b/main/cloudfoundry_client/v3/tasks.py index a92e42c..a7ce16e 100644 --- a/main/cloudfoundry_client/v3/tasks.py +++ b/main/cloudfoundry_client/v3/tasks.py @@ -1,21 +1,30 @@ -from typing import Optional +from typing import Optional, TYPE_CHECKING from cloudfoundry_client.v3.entities import EntityManager, Entity +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + class TaskManager(EntityManager): - def __init__(self, target_endpoint: str, client: 'CloudfoundryClient'): - super(TaskManager, self).__init__(target_endpoint, client, '/v3/tasks') + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(TaskManager, self).__init__(target_endpoint, client, "/v3/tasks") - def create(self, application_guid: str, command: str, - name: Optional[str] = None, disk_in_mb: Optional[int] = None, memory_in_mb: Optional[int] = None, - droplet_guid: Optional[str] = None) -> Entity: + def create( + self, + application_guid: str, + command: str, + name: Optional[str] = None, + disk_in_mb: Optional[int] = None, + memory_in_mb: Optional[int] = None, + droplet_guid: Optional[str] = None, + ) -> Entity: request = self._request(command=command) - request['name'] = name - request['disk_in_mb'] = disk_in_mb - request['memory_in_mb'] = memory_in_mb - request['droplet_guid'] = droplet_guid - return self._post('%s/v3/apps/%s/tasks' % (self.target_endpoint, application_guid), data=request) + request["name"] = name + request["disk_in_mb"] = disk_in_mb + request["memory_in_mb"] = memory_in_mb + request["droplet_guid"] = droplet_guid + return self._post("%s/v3/apps/%s/tasks" % (self.target_endpoint, application_guid), data=request) def cancel(self, task_guid: str) -> Entity: - return self._post('%s/v3/tasks/%s/actions/cancel' % (self.target_endpoint, task_guid)) + return self._post("%s/v3/tasks/%s/actions/cancel" % (self.target_endpoint, task_guid)) diff --git a/requirements.txt b/requirements.txt index 132a176..66d718f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,5 @@ protobuf==3.6.1 oauth2-client==1.2.1 websocket-client==0.54.0 PyYAML==5.3.1 +requests~=2.25.1 +setuptools~=49.2.1 \ No newline at end of file diff --git a/setup.py b/setup.py index e18f877..8eec4f1 100644 --- a/setup.py +++ b/setup.py @@ -3,31 +3,31 @@ import subprocess import sys -from setuptools import setup, find_packages, Command +from setuptools import setup, find_packages, Command, __version__ -src_dir = 'main' -package_directory = 'cloudfoundry_client' -package_name = 'cloudfoundry-client' -loggregator_dir = 'loggregator' +src_dir = "main" +package_directory = "cloudfoundry_client" +package_name = "cloudfoundry-client" +loggregator_dir = "loggregator" sys.path.insert(0, os.path.realpath(src_dir)) -version_file = '%s/%s/__init__.py' % (src_dir, package_directory) -with open(version_file, 'r') as f: +version_file = "%s/%s/__init__.py" % (src_dir, package_directory) +with open(version_file, "r") as f: for line in f.readlines(): - if line.find('__version__') >= 0: + if line.find("__version__") >= 0: exec(line) break else: - raise AssertionError('Failed to load version from %s' % version_file) + raise AssertionError("Failed to load version from %s" % version_file) def purge_sub_dir(path): shutil.rmtree(os.path.join(os.path.dirname(__file__), path)) -if 'test' in sys.argv[1:]: - print('%s added' % os.path.join(os.getcwd(), 'test')) - sys.path.append(os.path.join(os.getcwd(), 'test')) +if "test" in sys.argv[1:]: + print("%s added" % os.path.join(os.getcwd(), "test")) + sys.path.append(os.path.join(os.getcwd(), "test")) class GenerateCommand(Command): @@ -43,37 +43,46 @@ def finalize_options(self): def run(self): source_path = os.path.join(os.path.dirname(__file__), src_dir, package_directory, loggregator_dir) for file_protobuf in os.listdir(source_path): - if file_protobuf.endswith('.proto'): + if file_protobuf.endswith(".proto"): file_path = os.path.join(source_path, file_protobuf) - print('Generating %s from %s' % (file_protobuf, file_path)) - subprocess.call(['protoc', '-I', source_path, '--python_out=%s' % source_path, file_path]) + print("Generating %s from %s" % (file_protobuf, file_path)) + subprocess.call( + [ + "protoc", + "-I", + source_path, + "--python_out=%s" % source_path, + file_path, + ] + ) -setup(name=package_name, - version=__version__, - zip_safe=True, - packages=find_packages(where=src_dir), - author='Benjamin Einaudi', - author_email='antechrestos@gmail.com', - description='A client library for CloudFoundry', - long_description=open('README.rst').read(), - url='http://github.com/antechrestos/cf-python-client', - classifiers=[ - "Programming Language :: Python", - "Natural Language :: English", - "Operating System :: OS Independent", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Topic :: Communications", - ], - entry_points={ - 'console_scripts': [ - 'cloudfoundry-client = %s.main.main:main' % package_directory, - ] - }, - cmdclass=dict(generate=GenerateCommand), - package_dir={package_directory: '%s/%s' % (src_dir, package_directory)}, - install_requires=[requirement.rstrip(' \r\n') for requirement in open('requirements.txt')], - test_suite='test', - ) +setup( + name=package_name, + version=__version__, + zip_safe=True, + packages=find_packages(where=src_dir), + author="Benjamin Einaudi", + author_email="antechrestos@gmail.com", + description="A client library for CloudFoundry", + long_description=open("README.rst").read(), + url="http://github.com/antechrestos/cf-python-client", + classifiers=[ + "Programming Language :: Python", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Topic :: Communications", + ], + entry_points={ + "console_scripts": [ + "cloudfoundry-client = %s.main.main:main" % package_directory, + ] + }, + cmdclass=dict(generate=GenerateCommand), + package_dir={package_directory: "%s/%s" % (src_dir, package_directory)}, + install_requires=[requirement.rstrip(" \r\n") for requirement in open("requirements.txt")], + test_suite="test", +) diff --git a/test/abstract_test_case.py b/test/abstract_test_case.py index 93cbcda..511f02f 100644 --- a/test/abstract_test_case.py +++ b/test/abstract_test_case.py @@ -11,8 +11,8 @@ def mock_cloudfoundry_client_class(): - if not getattr(CloudFoundryClient, 'CLASS_MOCKED', False): - mocked_attributes = ['get', 'post', 'patch', 'put', 'delete'] + if not getattr(CloudFoundryClient, "CLASS_MOCKED", False): + mocked_attributes = ["get", "post", "patch", "put", "delete"] class MockClass(CredentialManager): def __init__(self, *args, **kwargs): @@ -21,15 +21,15 @@ def __init__(self, *args, **kwargs): setattr(self, attribute, MagicMock()) CloudFoundryClient.__bases__ = (MockClass,) - setattr(CloudFoundryClient, 'CLASS_MOCKED', True) + setattr(CloudFoundryClient, "CLASS_MOCKED", True) class AbstractTestCase(object): TARGET_ENDPOINT = "http://somewhere.org" AUTHORIZATION_ENDPOINT = "http://login.somewhere.org" TOKEN_ENDPOINT = "http://token.somewhere.org" - DOPPLER_ENDPOINT = 'wss://doppler.nd-cfapi.itn.ftgroup:443' - LOG_STREAM_ENDPOINT = 'https://log-stream.nd-cfapi.itn.ftgroup' + DOPPLER_ENDPOINT = "wss://doppler.nd-cfapi.itn.ftgroup:443" + LOG_STREAM_ENDPOINT = "https://log-stream.nd-cfapi.itn.ftgroup" API_V2_VERSION = "2.141.0" API_V3_VERSION = "3.76.0" @@ -38,7 +38,7 @@ def mock_client_class(cls): mock_cloudfoundry_client_class() def build_client(self): - with patch('cloudfoundry_client.client.requests') as fake_requests: + with patch("cloudfoundry_client.client.requests") as fake_requests: self._mock_info_calls(fake_requests) self.client = CloudFoundryClient(self.TARGET_ENDPOINT) @@ -46,47 +46,58 @@ def build_client(self): def _mock_info_calls(requests, with_doppler: bool = True, with_log_streams: bool = True): requests.get.side_effect = [ MockResponse( - '%s/v2/info' % AbstractTestCase.TARGET_ENDPOINT, + "%s/v2/info" % AbstractTestCase.TARGET_ENDPOINT, status_code=HTTPStatus.OK.value, - text=json.dumps(dict(authorization_endpoint=AbstractTestCase.AUTHORIZATION_ENDPOINT, - token_endpoint=AbstractTestCase.TOKEN_ENDPOINT))), + text=json.dumps( + dict( + authorization_endpoint=AbstractTestCase.AUTHORIZATION_ENDPOINT, + token_endpoint=AbstractTestCase.TOKEN_ENDPOINT, + ) + ), + ), MockResponse( - '%s/' % AbstractTestCase.TARGET_ENDPOINT, + "%s/" % AbstractTestCase.TARGET_ENDPOINT, status_code=HTTPStatus.OK.value, - text=json.dumps(dict(links={ - 'self': dict(href=AbstractTestCase.TARGET_ENDPOINT), - 'cloud_controller_v2': dict(href='%s/v2' % AbstractTestCase.TARGET_ENDPOINT, - meta=dict(version=AbstractTestCase.API_V2_VERSION)), - 'cloud_controller_v3': dict(href='%s/v3' % AbstractTestCase.TARGET_ENDPOINT, - meta=dict(version=AbstractTestCase.API_V3_VERSION)), - 'logging': dict(href=AbstractTestCase.DOPPLER_ENDPOINT) if with_doppler else None, - 'log_stream': dict(href=AbstractTestCase.LOG_STREAM_ENDPOINT) if with_log_streams else None, - 'app_ssh': dict(href='ssh.nd-cfapi.itn.ftgroup:80'), - 'uaa': dict(href='https://uaa.nd-cfapi.itn.ftgroup'), - 'network_policy_v0': dict( - href='https://api.nd-cfapi.itn.ftgroup/networking/v0/external'), - 'network_policy_v1': dict( - href='https://api.nd-cfapi.itn.ftgroup/networking/v1/external') - }))) + text=json.dumps( + dict( + links={ + "self": dict(href=AbstractTestCase.TARGET_ENDPOINT), + "cloud_controller_v2": dict( + href="%s/v2" % AbstractTestCase.TARGET_ENDPOINT, + meta=dict(version=AbstractTestCase.API_V2_VERSION), + ), + "cloud_controller_v3": dict( + href="%s/v3" % AbstractTestCase.TARGET_ENDPOINT, + meta=dict(version=AbstractTestCase.API_V3_VERSION), + ), + "logging": dict(href=AbstractTestCase.DOPPLER_ENDPOINT) if with_doppler else None, + "log_stream": dict(href=AbstractTestCase.LOG_STREAM_ENDPOINT) if with_log_streams else None, + "app_ssh": dict(href="ssh.nd-cfapi.itn.ftgroup:80"), + "uaa": dict(href="https://uaa.nd-cfapi.itn.ftgroup"), + "network_policy_v0": dict(href="https://api.nd-cfapi.itn.ftgroup/networking/v0/external"), + "network_policy_v1": dict(href="https://api.nd-cfapi.itn.ftgroup/networking/v1/external"), + } + ) + ), + ), ] @staticmethod def get_fixtures_path(*paths): - return os.path.join(os.path.dirname(__file__), 'fixtures', *paths) + return os.path.join(os.path.dirname(__file__), "fixtures", *paths) @staticmethod def mock_response(uri: str, status_code: HTTPStatus, headers: Optional[dict], *path_parts: str): if len(path_parts) > 0: file_name = path_parts[len(path_parts) - 1] - extension_idx = file_name.rfind('.') - binary_file = extension_idx >= 0 and file_name[extension_idx:] == '.bin' - with(open(AbstractTestCase.get_fixtures_path(*path_parts), - 'rb' if binary_file else 'r')) as f: - return MockResponse(url='%s%s' % (AbstractTestCase.TARGET_ENDPOINT, uri), - status_code=status_code.value, - text=f.read(), - headers=headers) + extension_idx = file_name.rfind(".") + binary_file = extension_idx >= 0 and file_name[extension_idx:] == ".bin" + with (open(AbstractTestCase.get_fixtures_path(*path_parts), "rb" if binary_file else "r")) as f: + return MockResponse( + url="%s%s" % (AbstractTestCase.TARGET_ENDPOINT, uri), + status_code=status_code.value, + text=f.read(), + headers=headers, + ) else: - return MockResponse('%s%s' % (AbstractTestCase.TARGET_ENDPOINT, uri), - status_code.value, - '') + return MockResponse("%s%s" % (AbstractTestCase.TARGET_ENDPOINT, uri), status_code.value, "") diff --git a/test/networking/v1/external/test_policies.py b/test/networking/v1/external/test_policies.py index 2dad659..581eb6b 100644 --- a/test/networking/v1/external/test_policies.py +++ b/test/networking/v1/external/test_policies.py @@ -14,33 +14,47 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = self.mock_response('/networking/v1/external/policies', - HTTPStatus.OK, - None, - 'networking', 'v1', 'external', 'policies', - 'GET_response.json') + self.client.get.return_value = self.mock_response( + "/networking/v1/external/policies", + HTTPStatus.OK, + None, + "networking", + "v1", + "external", + "policies", + "GET_response.json", + ) all_policies = [policy for policy in self.client.networking_v1_external.policies.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(2, len(all_policies)) - self.assertEqual(all_policies[0]['source']['id'], "1081ceac-f5c4-47a8-95e8-88e1e302efb5") - self.assertEqual(all_policies[0]['destination']['id'], "38f08df0-19df-4439-b4e9-61096d4301ea") + self.assertEqual(all_policies[0]["source"]["id"], "1081ceac-f5c4-47a8-95e8-88e1e302efb5") + self.assertEqual(all_policies[0]["destination"]["id"], "38f08df0-19df-4439-b4e9-61096d4301ea") def test_delete(self): - self.client.delete.return_value = self.mock_response('/networking/v1/external/policies/delete', - HTTPStatus.OK, - None) - policy = Policy(src_id='1081ceac-f5c4-47a8-95e8-88e1e302efb5', - dst_id='38f08df0-19df-4439-b4e9-61096d4301ea', - proto='tcp', - start_port=1234, - end_port=1234) - self.client.networking_v1_external.policies.delete([policy, ]) - self.client.delete.assert_called_with({'policies': [ - {'source': - {'id': '1081ceac-f5c4-47a8-95e8-88e1e302efb5'}, - 'destination': { - 'id': '38f08df0-19df-4439-b4e9-61096d4301ea', - 'ports': {'start': 1234, 'end': 1234}, - 'protocol': 'tcp'} - } - ]}) + self.client.delete.return_value = self.mock_response("/networking/v1/external/policies/delete", HTTPStatus.OK, None) + policy = Policy( + src_id="1081ceac-f5c4-47a8-95e8-88e1e302efb5", + dst_id="38f08df0-19df-4439-b4e9-61096d4301ea", + proto="tcp", + start_port=1234, + end_port=1234, + ) + self.client.networking_v1_external.policies.delete( + [ + policy, + ] + ) + self.client.delete.assert_called_with( + { + "policies": [ + { + "source": {"id": "1081ceac-f5c4-47a8-95e8-88e1e302efb5"}, + "destination": { + "id": "38f08df0-19df-4439-b4e9-61096d4301ea", + "ports": {"start": 1234, "end": 1234}, + "protocol": "tcp", + }, + } + ] + } + ) diff --git a/test/operations/push/test_cf_ignore.py b/test/operations/push/test_cf_ignore.py index ae4124a..d418d7a 100644 --- a/test/operations/push/test_cf_ignore.py +++ b/test/operations/push/test_cf_ignore.py @@ -6,47 +6,46 @@ class TestCfIgnore(TestCase): - def test_open_cfignore_file(self): - with patch("builtins.open", mock_open(read_data="*.log")) as mock_file, \ - patch('os.path.isfile', create=True) as mock_isfile: + with patch("builtins.open", mock_open(read_data="*.log")) as mock_file, patch( + "os.path.isfile", create=True + ) as mock_isfile: mock_isfile.__return_value__ = True - application_path = '/some/path' + application_path = "/some/path" CfIgnore(application_path) - mock_file.assert_called_with(os.path.join(application_path, '.cfignore'), 'r') + mock_file.assert_called_with(os.path.join(application_path, ".cfignore"), "r") def test_ignore_wildcard_resources(self): - with patch("builtins.open", mock_open(read_data="*.log")), \ - patch('os.path.isfile', create=True) as mock_isfile: + with patch("builtins.open", mock_open(read_data="*.log")), patch("os.path.isfile", create=True) as mock_isfile: mock_isfile.__return_value__ = True - cf_ignore = CfIgnore('/some/path') + cf_ignore = CfIgnore("/some/path") self.assertTrue(cf_ignore.is_entry_ignored("toto.log")) self.assertTrue(cf_ignore.is_entry_ignored("/some/other/path/toto.log")) def test_ignore_directory(self): - with patch("builtins.open", mock_open(read_data="ignored/directory/")), \ - patch('os.path.isfile', create=True) as mock_isfile: + with patch("builtins.open", mock_open(read_data="ignored/directory/")), patch( + "os.path.isfile", create=True + ) as mock_isfile: mock_isfile.__return_value__ = True - cf_ignore = CfIgnore('/some/path') + cf_ignore = CfIgnore("/some/path") self.assertTrue(cf_ignore.is_entry_ignored("ignored/directory/resource.file")) self.assertTrue(cf_ignore.is_entry_ignored("/ignored/directory/resource.file")) - self.assertTrue( - cf_ignore.is_entry_ignored("/some/sub/directory/containing/ignored/directory/resource.file")) + self.assertTrue(cf_ignore.is_entry_ignored("/some/sub/directory/containing/ignored/directory/resource.file")) # File in fact - self.assertFalse(cf_ignore.is_entry_ignored('/ignored/directory')) + self.assertFalse(cf_ignore.is_entry_ignored("/ignored/directory")) def test_ignore_file_with_directory(self): - with patch("builtins.open", mock_open(read_data="ignored/directory/resource.file")), \ - patch('os.path.isfile', create=True) as mock_isfile: + with patch("builtins.open", mock_open(read_data="ignored/directory/resource.file")), patch( + "os.path.isfile", create=True + ) as mock_isfile: mock_isfile.__return_value__ = True - cf_ignore = CfIgnore('/some/path') + cf_ignore = CfIgnore("/some/path") self.assertTrue(cf_ignore.is_entry_ignored("ignored/directory/resource.file")) self.assertTrue(cf_ignore.is_entry_ignored("/ignored/directory/resource.file")) - self.assertTrue( - cf_ignore.is_entry_ignored("/some/sub/directory/containing/ignored/directory/resource.file")) + self.assertTrue(cf_ignore.is_entry_ignored("/some/sub/directory/containing/ignored/directory/resource.file")) # File in fact - self.assertFalse(cf_ignore.is_entry_ignored('ignored/resource.file')) + self.assertFalse(cf_ignore.is_entry_ignored("ignored/resource.file")) diff --git a/test/operations/push/test_file_helper.py b/test/operations/push/test_file_helper.py index b21ca8a..fbf483e 100644 --- a/test/operations/push/test_file_helper.py +++ b/test/operations/push/test_file_helper.py @@ -17,30 +17,30 @@ def test_unzip(self): self.assertFileUnzipped() def test_unzip_with_existing_output_subdir(self): - os.makedirs(os.path.join(self.output_dirpath, 'some_dir', 'subdir')) + os.makedirs(os.path.join(self.output_dirpath, "some_dir", "subdir")) self.unzip() self.assertFileUnzipped() def unzip(self): - FileHelper.unzip(os.path.join(self.input_dirpath, 'myzip.zip'), self.output_dirpath) + FileHelper.unzip(os.path.join(self.input_dirpath, "myzip.zip"), self.output_dirpath) def zip_some_data(self): input_dirpath = self.prepare_data_to_zip() - with zipfile.ZipFile(os.path.join(input_dirpath, 'myzip.zip'), 'w', zipfile.ZIP_DEFLATED) as myzip: - myzip.write(os.path.join(input_dirpath, 'file.txt'), 'file.txt') - myzip.write(os.path.join(input_dirpath, 'some_dir'), 'some_dir') - myzip.write(os.path.join(input_dirpath, 'some_dir', 'subdir'), 'some_dir/subdir') + with zipfile.ZipFile(os.path.join(input_dirpath, "myzip.zip"), "w", zipfile.ZIP_DEFLATED) as myzip: + myzip.write(os.path.join(input_dirpath, "file.txt"), "file.txt") + myzip.write(os.path.join(input_dirpath, "some_dir"), "some_dir") + myzip.write(os.path.join(input_dirpath, "some_dir", "subdir"), "some_dir/subdir") return input_dirpath def prepare_data_to_zip(self): input_dirpath = tempfile.mkdtemp() - shutil.copyfile(__file__, os.path.join(input_dirpath, 'file.txt')) - os.makedirs(os.path.join(input_dirpath, 'some_dir', 'subdir')) - shutil.copyfile(__file__, os.path.join(input_dirpath, 'some_dir', 'file.txt')) + shutil.copyfile(__file__, os.path.join(input_dirpath, "file.txt")) + os.makedirs(os.path.join(input_dirpath, "some_dir", "subdir")) + shutil.copyfile(__file__, os.path.join(input_dirpath, "some_dir", "file.txt")) return input_dirpath def assertFileUnzipped(self): - self.assertTrue(os.path.isfile(os.path.join(self.output_dirpath, 'file.txt'))) - self.assertTrue(os.path.isdir(os.path.join(self.output_dirpath, 'some_dir'))) - self.assertTrue(os.path.isdir(os.path.join(self.output_dirpath, 'some_dir', 'subdir'))) + self.assertTrue(os.path.isfile(os.path.join(self.output_dirpath, "file.txt"))) + self.assertTrue(os.path.isdir(os.path.join(self.output_dirpath, "some_dir"))) + self.assertTrue(os.path.isdir(os.path.join(self.output_dirpath, "some_dir", "subdir"))) diff --git a/test/operations/push/test_push.py b/test/operations/push/test_push.py index 9c77127..2e70337 100644 --- a/test/operations/push/test_push.py +++ b/test/operations/push/test_push.py @@ -16,36 +16,38 @@ def setUp(self): self.build_client() def test_split_route_with_port_and_path(self): - domain, port, path = PushOperation._split_route(dict(route='foo-((suffix)).apps.internal:666/some/path')) - self.assertEqual('foo-((suffix)).apps.internal', domain) + domain, port, path = PushOperation._split_route(dict(route="foo-((suffix)).apps.internal:666/some/path")) + self.assertEqual("foo-((suffix)).apps.internal", domain) self.assertEqual(666, port) - self.assertEqual('/some/path', path) + self.assertEqual("/some/path", path) def test_split_route_without_port_and_path(self): - domain, port, path = PushOperation._split_route(dict(route='foo-((suffix)).apps.internal')) - self.assertEqual('foo-((suffix)).apps.internal', domain) + domain, port, path = PushOperation._split_route(dict(route="foo-((suffix)).apps.internal")) + self.assertEqual("foo-((suffix)).apps.internal", domain) self.assertIsNone(port) - self.assertEqual('', path) + self.assertEqual("", path) def test_split_route_without_port_path(self): - domain, port, path = PushOperation._split_route(dict(route='foo-((suffix)).apps.internal/path')) - self.assertEqual('foo-((suffix)).apps.internal', domain) + domain, port, path = PushOperation._split_route(dict(route="foo-((suffix)).apps.internal/path")) + self.assertEqual("foo-((suffix)).apps.internal", domain) self.assertIsNone(port) - self.assertEqual('/path', path) + self.assertEqual("/path", path) def test_split_route_without_path(self): - domain, port, path = PushOperation._split_route(dict(route='foo-((suffix)).apps.internal:666')) - self.assertEqual('foo-((suffix)).apps.internal', domain) + domain, port, path = PushOperation._split_route(dict(route="foo-((suffix)).apps.internal:666")) + self.assertEqual("foo-((suffix)).apps.internal", domain) self.assertEqual(666, port) - self.assertEqual('', path) + self.assertEqual("", path) def test_to_host_should_remove_unwanted_characters(self): - host = PushOperation._to_host('idzone-3.0.7-rec-tb1_bobby') - self.assertEquals('idzone-307-rec-tb1-bobby', host) - - @patch.object(sys, 'argv', - ['main', 'push_app', AbstractTestCase.get_fixtures_path('fake', 'manifest_main.yml'), '-space_guid', - 'space_id']) + host = PushOperation._to_host("idzone-3.0.7-rec-tb1_bobby") + self.assertEquals("idzone-307-rec-tb1-bobby", host) + + @patch.object( + sys, + "argv", + ["main", "push_app", AbstractTestCase.get_fixtures_path("fake", "manifest_main.yml"), "-space_guid", "space_id"], + ) def test_main_push(self): class FakeOperation(object): def __init__(self): @@ -53,8 +55,8 @@ def __init__(self): client = object() push_operation = FakeOperation() - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: client), \ - patch('cloudfoundry_client.main.operation_commands.PushOperation', new=lambda c: push_operation): + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: client), patch( + "cloudfoundry_client.main.operation_commands.PushOperation", new=lambda c: push_operation + ): main.main() - push_operation.push.assert_called_with('space_id', self.get_fixtures_path('fake', 'manifest_main.yml')) + push_operation.push.assert_called_with("space_id", self.get_fixtures_path("fake", "manifest_main.yml")) diff --git a/test/operations/push/validation/test_manifest_reader.py b/test/operations/push/validation/test_manifest_reader.py index 0e9e0fc..25995c3 100644 --- a/test/operations/push/validation/test_manifest_reader.py +++ b/test/operations/push/validation/test_manifest_reader.py @@ -6,96 +6,115 @@ class TestManifestReader(unittest.TestCase): def test_empty_manifest_should_raise_exception(self): - manifest_file = os.path.join(os.path.dirname(__file__), '..', '..', '..', 'fixtures', 'operations', - 'manifest_empty.yml') + manifest_file = os.path.join(os.path.dirname(__file__), "..", "..", "..", "fixtures", "operations", "manifest_empty.yml") self.assertRaises(AssertionError, lambda: ManifestReader.load_application_manifests(manifest_file)) def test_manifest_should_be_read(self): - manifest_file = os.path.join(os.path.dirname(__file__), '..', '..', '..', 'fixtures', 'operations', - 'manifest.yml') + manifest_file = os.path.join(os.path.dirname(__file__), "..", "..", "..", "fixtures", "operations", "manifest.yml") applications = ManifestReader.load_application_manifests(manifest_file) self.assertEqual(1, len(applications)) - self.assertEqual(dict(docker=dict(username='the-user', password='P@SsW0r$', image='some-image'), - name='the-name', routes=[dict(route='first-route'), dict(route='second-route')]), - applications[0]) + self.assertEqual( + dict( + docker=dict(username="the-user", password="P@SsW0r$", image="some-image"), + name="the-name", + routes=[dict(route="first-route"), dict(route="second-route")], + ), + applications[0], + ) def test_complex_manifest_should_be_read(self): - manifest_file = os.path.join(os.path.dirname(__file__), '..', '..', '..', 'fixtures', 'operations', - 'manifest_complex.yml') + manifest_file = os.path.join( + os.path.dirname(__file__), "..", "..", "..", "fixtures", "operations", "manifest_complex.yml" + ) applications = ManifestReader.load_application_manifests(manifest_file) self.assertEqual(2, len(applications)) - self.assertEqual(dict(name='bigapp', buildpacks=['staticfile_buildpack'], memory=1024, - path=os.path.abspath(os.path.join(os.path.dirname(manifest_file), 'big'))), - applications[0]) - self.assertEqual(dict(name='smallapp', buildpacks=['staticfile_buildpack'], memory=256, - path=os.path.abspath(os.path.join(os.path.dirname(manifest_file), 'small'))), - applications[1]) + self.assertEqual( + dict( + name="bigapp", + buildpacks=["staticfile_buildpack"], + memory=1024, + path=os.path.abspath(os.path.join(os.path.dirname(manifest_file), "big")), + ), + applications[0], + ) + self.assertEqual( + dict( + name="smallapp", + buildpacks=["staticfile_buildpack"], + memory=256, + path=os.path.abspath(os.path.join(os.path.dirname(manifest_file), "small")), + ), + applications[1], + ) def test_name_should_be_set(self): - manifest = dict(path='test/') - self.assertRaises(AssertionError, lambda: ManifestReader._validate_application_manifest('.', manifest)) + manifest = dict(path="test/") + self.assertRaises(AssertionError, lambda: ManifestReader._validate_application_manifest(".", manifest)) def test_application_should_declare_either_path_or_docker(self): - manifest = dict(name='the-name', docker=dict(), path='test/') - self.assertRaises(AssertionError, lambda: ManifestReader._validate_application_manifest('.', manifest)) + manifest = dict(name="the-name", docker=dict(), path="test/") + self.assertRaises(AssertionError, lambda: ManifestReader._validate_application_manifest(".", manifest)) def test_application_should_declare_at_least_path_or_docker(self): - manifest = dict(name='the-name', routes=[], environment=dict()) - self.assertRaises(AssertionError, lambda: ManifestReader._validate_application_manifest('.', manifest)) + manifest = dict(name="the-name", routes=[], environment=dict()) + self.assertRaises(AssertionError, lambda: ManifestReader._validate_application_manifest(".", manifest)) def test_deprecated_entries_should_not_be_set(self): - for deprecated in ['host', 'hosts', 'domain', 'domains', 'no-hostname']: - manifest = dict(name='the-name', path='test/') - manifest[deprecated] = 'some-value' - self.assertRaises(AssertionError, lambda: ManifestReader._validate_application_manifest('.', manifest)) + for deprecated in ["host", "hosts", "domain", "domains", "no-hostname"]: + manifest = dict(name="the-name", path="test/") + manifest[deprecated] = "some-value" + self.assertRaises(AssertionError, lambda: ManifestReader._validate_application_manifest(".", manifest)) def test_docker_manifest_should_declare_buildpack_or_image(self): - manifest = dict(name='the-name', docker=dict(image='some-image', buildpack='some-buildpack')) - self.assertRaises(AssertionError, lambda: ManifestReader._validate_application_manifest('.', manifest)) + manifest = dict(name="the-name", docker=dict(image="some-image", buildpack="some-buildpack")) + self.assertRaises(AssertionError, lambda: ManifestReader._validate_application_manifest(".", manifest)) def test_username_should_be_set_if_password_is(self): - manifest = dict(name='the-name', docker=dict(image='some-image', password='P@SsW0r$')) - self.assertRaises(AssertionError, lambda: ManifestReader._validate_application_manifest('.', manifest)) + manifest = dict(name="the-name", docker=dict(image="some-image", password="P@SsW0r$")) + self.assertRaises(AssertionError, lambda: ManifestReader._validate_application_manifest(".", manifest)) def test_password_should_be_set_if_username_is(self): - manifest = dict(name='the-name', docker=dict(image='some-image', username='the-user')) - self.assertRaises(AssertionError, lambda: ManifestReader._validate_application_manifest('.', manifest)) + manifest = dict(name="the-name", docker=dict(image="some-image", username="the-user")) + self.assertRaises(AssertionError, lambda: ManifestReader._validate_application_manifest(".", manifest)) def test_username_and_password_are_set_when_image_is(self): - manifest = dict(name='the-name', docker=dict(username='the-user', password='P@SsW0r$')) - self.assertRaises(AssertionError, lambda: ManifestReader._validate_application_manifest('.', manifest)) + manifest = dict(name="the-name", docker=dict(username="the-user", password="P@SsW0r$")) + self.assertRaises(AssertionError, lambda: ManifestReader._validate_application_manifest(".", manifest)) def test_routes_should_be_an_object_with_attribute(self): - manifest = dict(name='the-name', path='test/', routes=['a route']) - self.assertRaises(AssertionError, lambda: ManifestReader._validate_application_manifest('.', manifest)) - manifest = dict(name='the-name', path='test/', routes=[dict(invalid_attribute='any-value')]) - self.assertRaises(AssertionError, lambda: ManifestReader._validate_application_manifest('.', manifest)) + manifest = dict(name="the-name", path="test/", routes=["a route"]) + self.assertRaises(AssertionError, lambda: ManifestReader._validate_application_manifest(".", manifest)) + manifest = dict(name="the-name", path="test/", routes=[dict(invalid_attribute="any-value")]) + self.assertRaises(AssertionError, lambda: ManifestReader._validate_application_manifest(".", manifest)) def test_valid_application_with_path_and_routes(self): - manifest = dict(name='the-name', path='test/', routes=[dict(route='first-route'), dict(route='second-route')]) - ManifestReader._validate_application_manifest('.', manifest) + manifest = dict(name="the-name", path="test/", routes=[dict(route="first-route"), dict(route="second-route")]) + ManifestReader._validate_application_manifest(".", manifest) def test_valid_application_with_docker_and_routes(self): - manifest = dict(docker=dict(username='the-user', password='P@SsW0r$', image='some-image'), - name='the-name', routes=[dict(route='first-route'), dict(route='second-route')]) - ManifestReader._validate_application_manifest('.', manifest) + manifest = dict( + docker=dict(username="the-user", password="P@SsW0r$", image="some-image"), + name="the-name", + routes=[dict(route="first-route"), dict(route="second-route")], + ) + ManifestReader._validate_application_manifest(".", manifest) def path_should_be_set_as_absolute(self): - manifest = dict(name='the-name', path='test/') - ManifestReader._validate_application_manifest('.', manifest) - self.assertEqual(os.path.abspath('test'), manifest['path']) + manifest = dict(name="the-name", path="test/") + ManifestReader._validate_application_manifest(".", manifest) + self.assertEqual(os.path.abspath("test"), manifest["path"]) def test_memory_in_kb(self): - manifest = dict(memory='2048KB') + manifest = dict(memory="2048KB") ManifestReader._convert_memory(manifest) - self.assertEqual(2, manifest['memory']) + self.assertEqual(2, manifest["memory"]) def test_memory_in_mb(self): - manifest = dict(memory='2048MB') + manifest = dict(memory="2048MB") ManifestReader._convert_memory(manifest) - self.assertEqual(2048, manifest['memory']) + self.assertEqual(2048, manifest["memory"]) def test_memory_in_gb(self): - manifest = dict(memory='1G') + manifest = dict(memory="1G") ManifestReader._convert_memory(manifest) - self.assertEqual(1024, manifest['memory']) + self.assertEqual(1024, manifest["memory"]) diff --git a/test/test_client.py b/test/test_client.py index 5c0fde0..2d73c1f 100644 --- a/test/test_client.py +++ b/test/test_client.py @@ -9,7 +9,10 @@ from fake_requests import MockResponse, MockSession, FakeRequests -class TestCloudfoundryClient(unittest.TestCase, AbstractTestCase, ): +class TestCloudfoundryClient( + unittest.TestCase, + AbstractTestCase, +): @classmethod def setUpClass(cls): cls.mock_client_class() @@ -17,100 +20,111 @@ def setUpClass(cls): def test_build_client_when_no_log_stream(self): requests = FakeRequests() session = MockSession() - with patch('oauth2_client.credentials_manager.requests', new=requests), \ - patch('cloudfoundry_client.client.requests', new=requests): + with patch("oauth2_client.credentials_manager.requests", new=requests), patch( + "cloudfoundry_client.client.requests", new=requests + ): requests.Session.return_value = session self._mock_info_calls(requests, with_log_streams=False) - client = CloudFoundryClient(self.TARGET_ENDPOINT, token_format='opaque') + client = CloudFoundryClient(self.TARGET_ENDPOINT, token_format="opaque") self.assertRaises(NotImplementedError, lambda: client.rlpgateway) def test_build_client_when_no_doppler(self): requests = FakeRequests() session = MockSession() - with patch('oauth2_client.credentials_manager.requests', new=requests), \ - patch('cloudfoundry_client.client.requests', new=requests): + with patch("oauth2_client.credentials_manager.requests", new=requests), patch( + "cloudfoundry_client.client.requests", new=requests + ): requests.Session.return_value = session self._mock_info_calls(requests, with_doppler=False) - client = CloudFoundryClient(self.TARGET_ENDPOINT, token_format='opaque') + client = CloudFoundryClient(self.TARGET_ENDPOINT, token_format="opaque") self.assertRaises(NotImplementedError, lambda: client.doppler) def test_grant_password_request_with_token_format_opaque(self): requests = FakeRequests() session = MockSession() - with patch('oauth2_client.credentials_manager.requests', new=requests), \ - patch('cloudfoundry_client.client.requests', new=requests): + with patch("oauth2_client.credentials_manager.requests", new=requests), patch( + "cloudfoundry_client.client.requests", new=requests + ): requests.Session.return_value = session self._mock_info_calls(requests) - requests.post.return_value = MockResponse('%s/oauth/token' % self.AUTHORIZATION_ENDPOINT, - status_code=HTTPStatus.OK.value, - text=json.dumps(dict(access_token='access-token', - refresh_token='refresh-token'))) - client = CloudFoundryClient(self.TARGET_ENDPOINT, token_format='opaque') - client.init_with_user_credentials('somebody', 'p@s$w0rd') - self.assertEqual('Bearer access-token', session.headers.get('Authorization')) - requests.post.assert_called_with(requests.post.return_value.url, - data=dict(grant_type='password', - username='somebody', - scope='', - password='p@s$w0rd', - token_format='opaque'), - headers=dict(Accept='application/json', Authorization='Basic Y2Y6'), - proxies=dict(http='', https=''), - verify=True) + requests.post.return_value = MockResponse( + "%s/oauth/token" % self.AUTHORIZATION_ENDPOINT, + status_code=HTTPStatus.OK.value, + text=json.dumps(dict(access_token="access-token", refresh_token="refresh-token")), + ) + client = CloudFoundryClient(self.TARGET_ENDPOINT, token_format="opaque") + client.init_with_user_credentials("somebody", "p@s$w0rd") + self.assertEqual("Bearer access-token", session.headers.get("Authorization")) + requests.post.assert_called_with( + requests.post.return_value.url, + data=dict(grant_type="password", username="somebody", scope="", password="p@s$w0rd", token_format="opaque"), + headers=dict(Accept="application/json", Authorization="Basic Y2Y6"), + proxies=dict(http="", https=""), + verify=True, + ) def test_refresh_request_with_token_format_opaque(self): requests = FakeRequests() session = MockSession() - with patch('oauth2_client.credentials_manager.requests', new=requests), \ - patch('cloudfoundry_client.client.requests', new=requests): + with patch("oauth2_client.credentials_manager.requests", new=requests), patch( + "cloudfoundry_client.client.requests", new=requests + ): requests.Session.return_value = session self._mock_info_calls(requests) - requests.post.return_value = MockResponse('%s/oauth/token' % self.AUTHORIZATION_ENDPOINT, - status_code=HTTPStatus.OK.value, - text=json.dumps(dict(access_token='access-token', - refresh_token='refresh-token'))) - client = CloudFoundryClient(self.TARGET_ENDPOINT, token_format='opaque') - client.init_with_token('refresh-token') - self.assertEqual('Bearer access-token', session.headers.get('Authorization')) - requests.post.assert_called_with(requests.post.return_value.url, - data=dict(grant_type='refresh_token', - scope='', - refresh_token='refresh-token', - token_format='opaque'), - headers=dict(Accept='application/json', Authorization='Basic Y2Y6'), - proxies=dict(http='', https=''), - verify=True) + requests.post.return_value = MockResponse( + "%s/oauth/token" % self.AUTHORIZATION_ENDPOINT, + status_code=HTTPStatus.OK.value, + text=json.dumps(dict(access_token="access-token", refresh_token="refresh-token")), + ) + client = CloudFoundryClient(self.TARGET_ENDPOINT, token_format="opaque") + client.init_with_token("refresh-token") + self.assertEqual("Bearer access-token", session.headers.get("Authorization")) + requests.post.assert_called_with( + requests.post.return_value.url, + data=dict(grant_type="refresh_token", scope="", refresh_token="refresh-token", token_format="opaque"), + headers=dict(Accept="application/json", Authorization="Basic Y2Y6"), + proxies=dict(http="", https=""), + verify=True, + ) def test_grant_password_request_with_login_hint(self): requests = FakeRequests() session = MockSession() - with patch('oauth2_client.credentials_manager.requests', new=requests), \ - patch('cloudfoundry_client.client.requests', new=requests): + with patch("oauth2_client.credentials_manager.requests", new=requests), patch( + "cloudfoundry_client.client.requests", new=requests + ): requests.Session.return_value = session self._mock_info_calls(requests) - requests.post.return_value = MockResponse('%s/oauth/token' % self.AUTHORIZATION_ENDPOINT, - status_code=HTTPStatus.OK.value, - text=json.dumps(dict(access_token='access-token', - refresh_token='refresh-token'))) - client = CloudFoundryClient(self.TARGET_ENDPOINT, login_hint=quote(json.dumps(dict(origin='uaa'), - separators=(',', ':')))) - client.init_with_user_credentials('somebody', 'p@s$w0rd') - self.assertEqual('Bearer access-token', session.headers.get('Authorization')) - requests.post.assert_called_with(requests.post.return_value.url, - data=dict(grant_type='password', - username='somebody', - scope='', - password='p@s$w0rd', - login_hint='%7B%22origin%22%3A%22uaa%22%7D'), - headers=dict(Accept='application/json', Authorization='Basic Y2Y6'), - proxies=dict(http='', https=''), - verify=True) + requests.post.return_value = MockResponse( + "%s/oauth/token" % self.AUTHORIZATION_ENDPOINT, + status_code=HTTPStatus.OK.value, + text=json.dumps(dict(access_token="access-token", refresh_token="refresh-token")), + ) + client = CloudFoundryClient( + self.TARGET_ENDPOINT, login_hint=quote(json.dumps(dict(origin="uaa"), separators=(",", ":"))) + ) + client.init_with_user_credentials("somebody", "p@s$w0rd") + self.assertEqual("Bearer access-token", session.headers.get("Authorization")) + requests.post.assert_called_with( + requests.post.return_value.url, + data=dict( + grant_type="password", + username="somebody", + scope="", + password="p@s$w0rd", + login_hint="%7B%22origin%22%3A%22uaa%22%7D", + ), + headers=dict(Accept="application/json", Authorization="Basic Y2Y6"), + proxies=dict(http="", https=""), + verify=True, + ) def test_get_info(self): requests = FakeRequests() session = MockSession() - with patch('oauth2_client.credentials_manager.requests', new=requests), \ - patch('cloudfoundry_client.client.requests', new=requests): + with patch("oauth2_client.credentials_manager.requests", new=requests), patch( + "cloudfoundry_client.client.requests", new=requests + ): requests.Session.return_value = session self._mock_info_calls(requests) info = CloudFoundryClient._get_info(self.TARGET_ENDPOINT) @@ -120,16 +134,22 @@ def test_get_info(self): self.assertEqual(info.log_stream_endpoint, self.LOG_STREAM_ENDPOINT) def test_invalid_token_v3(self): - response = MockResponse('http://some-cf-url', 401, text=json.dumps(dict( - errors=[dict(code=666, title='Some-Error', detail='Error detail'), - dict(code=1000, title='CF-InvalidAuthToken', detail='Invalid token')] - ))) + response = MockResponse( + "http://some-cf-url", + 401, + text=json.dumps( + dict( + errors=[ + dict(code=666, title="Some-Error", detail="Error detail"), + dict(code=1000, title="CF-InvalidAuthToken", detail="Invalid token"), + ] + ) + ), + ) result = CloudFoundryClient._is_token_expired(response) self.assertTrue(result) def test_invalid_token_v2(self): - response = MockResponse('http://some-cf-url', 401, - text=json.dumps(dict(code=1000, error_code='CF-InvalidAuthToken') - )) + response = MockResponse("http://some-cf-url", 401, text=json.dumps(dict(code=1000, error_code="CF-InvalidAuthToken"))) result = CloudFoundryClient._is_token_expired(response) self.assertTrue(result) diff --git a/test/test_request_object.py b/test/test_request_object.py index e443048..4562f24 100644 --- a/test/test_request_object.py +++ b/test/test_request_object.py @@ -4,14 +4,13 @@ class TestRequest(unittest.TestCase): - def test_mandatory_should_be_present_even_when_none(self): request = Request(mandatory=None) - self.assertTrue('mandatory' in request) - self.assertIsNone(request['mandatory']) + self.assertTrue("mandatory" in request) + self.assertIsNone(request["mandatory"]) def test_optional_should_not_be_present_when_none(self): - request = Request(mandatory='value') - request['optional'] = None - self.assertEqual('value', request['mandatory']) - self.assertTrue('optional' not in request) + request = Request(mandatory="value") + request["optional"] = None + self.assertEqual("value", request["mandatory"]) + self.assertTrue("optional" not in request) diff --git a/test/v2/test_apps.py b/test/v2/test_apps.py index 91ef838..59cdce3 100644 --- a/test/v2/test_apps.py +++ b/test/v2/test_apps.py @@ -18,290 +18,258 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = self.mock_response('/v2/apps', - HTTPStatus.OK, - None, - 'v2', 'apps', 'GET_response.json') + self.client.get.return_value = self.mock_response("/v2/apps", HTTPStatus.OK, None, "v2", "apps", "GET_response.json") all_applications = [application for application in self.client.v2.apps.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(len(all_applications), 3) - print('test_list - Application - %s' % str(all_applications[0])) - self.assertEqual(all_applications[0]['entity']['name'], "name-423") + print("test_list - Application - %s" % str(all_applications[0])) + self.assertEqual(all_applications[0]["entity"]["name"], "name-423") def test_list_filtered(self): self.client.get.return_value = self.mock_response( - '/v2/apps?q=name%3Aapplication_name&results-per-page=1&q=space_guid%3Aspace_guid', + "/v2/apps?q=name%3Aapplication_name&results-per-page=1&q=space_guid%3Aspace_guid", HTTPStatus.OK, None, - 'v2', 'apps', 'GET_space_guid_name_response.json') - application = self.client.v2.apps.get_first(space_guid='space_guid', name='application_name') + "v2", + "apps", + "GET_space_guid_name_response.json", + ) + application = self.client.v2.apps.get_first(space_guid="space_guid", name="application_name") self.client.get.assert_called_with(self.client.get.return_value.url) self.assertIsNotNone(application) def test_get_env(self): self.client.get.return_value = self.mock_response( - '/v2/apps/app_id/env', - HTTPStatus.OK, - None, - 'v2', 'apps', 'GET_{id}_env_response.json') - application = self.client.v2.apps.get_env('app_id') + "/v2/apps/app_id/env", HTTPStatus.OK, None, "v2", "apps", "GET_{id}_env_response.json" + ) + application = self.client.v2.apps.get_env("app_id") self.client.get.assert_called_with(self.client.get.return_value.url) self.assertIsNotNone(application) def test_get_instances(self): self.client.get.return_value = self.mock_response( - '/v2/apps/app_id/instances', - HTTPStatus.OK, - None, - 'v2', 'apps', 'GET_{id}_instances_response.json') - application = self.client.v2.apps.get_instances('app_id') + "/v2/apps/app_id/instances", HTTPStatus.OK, None, "v2", "apps", "GET_{id}_instances_response.json" + ) + application = self.client.v2.apps.get_instances("app_id") self.client.get.assert_called_with(self.client.get.return_value.url) self.assertIsNotNone(application) def test_get_stats(self): self.client.get.return_value = self.mock_response( - '/v2/apps/app_id/stats', - HTTPStatus.OK, - None, - 'v2', 'apps', 'GET_{id}_stats_response.json') - application = self.client.v2.apps.get_stats('app_id') + "/v2/apps/app_id/stats", HTTPStatus.OK, None, "v2", "apps", "GET_{id}_stats_response.json" + ) + application = self.client.v2.apps.get_stats("app_id") self.client.get.assert_called_with(self.client.get.return_value.url) self.assertIsNotNone(application) def test_associate_route(self): - self.client.put.return_value = self.mock_response('/v2/apps/app_id/routes/route_id', HTTPStatus.CREATED, None, - 'v2', 'apps', 'PUT_{id}_routes_{route_id}_response.json') - self.client.v2.apps.associate_route('app_id', 'route_id') + self.client.put.return_value = self.mock_response( + "/v2/apps/app_id/routes/route_id", HTTPStatus.CREATED, None, "v2", "apps", "PUT_{id}_routes_{route_id}_response.json" + ) + self.client.v2.apps.associate_route("app_id", "route_id") self.client.put.assert_called_with(self.client.put.return_value.url, json=None) def test_list_routes(self): self.client.get.return_value = self.mock_response( - '/v2/apps/app_id/routes?q=route_guid%3Aroute_id', - HTTPStatus.OK, - None, - 'v2', 'apps', 'GET_{id}_routes_response.json') - cpt = reduce(lambda increment, _: increment + 1, - self.client.v2.apps.list_routes('app_id', route_guid='route_id'), 0) - for route in self.client.v2.apps.list_routes('app_id', route_guid='route_id'): + "/v2/apps/app_id/routes?q=route_guid%3Aroute_id", HTTPStatus.OK, None, "v2", "apps", "GET_{id}_routes_response.json" + ) + cpt = reduce(lambda increment, _: increment + 1, self.client.v2.apps.list_routes("app_id", route_guid="route_id"), 0) + for route in self.client.v2.apps.list_routes("app_id", route_guid="route_id"): print(route) self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(cpt, 1) def test_remove_route(self): - self.client.delete.return_value = self.mock_response('/v2/apps/app_id/routes/route_id', HTTPStatus.NO_CONTENT, - None) - self.client.v2.apps.remove_route('app_id', 'route_id') + self.client.delete.return_value = self.mock_response("/v2/apps/app_id/routes/route_id", HTTPStatus.NO_CONTENT, None) + self.client.v2.apps.remove_route("app_id", "route_id") self.client.delete.assert_called_with(self.client.delete.return_value.url) def test_list_service_bindings(self): self.client.get.return_value = self.mock_response( - '/v2/apps/app_id/service_bindings', - HTTPStatus.OK, - None, - 'v2', 'apps', 'GET_{id}_service_bindings_response.json') - cpt = reduce(lambda increment, _: increment + 1, - self.client.v2.apps.list_service_bindings('app_id'), 0) + "/v2/apps/app_id/service_bindings", HTTPStatus.OK, None, "v2", "apps", "GET_{id}_service_bindings_response.json" + ) + cpt = reduce(lambda increment, _: increment + 1, self.client.v2.apps.list_service_bindings("app_id"), 0) self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(cpt, 1) def test_get_sumary(self): self.client.get.return_value = self.mock_response( - '/v2/apps/app_id/summary', - HTTPStatus.OK, - None, - 'v2', 'apps', 'GET_{id}_summary_response.json') - application = self.client.v2.apps.get_summary('app_id') + "/v2/apps/app_id/summary", HTTPStatus.OK, None, "v2", "apps", "GET_{id}_summary_response.json" + ) + application = self.client.v2.apps.get_summary("app_id") self.client.get.assert_called_with(self.client.get.return_value.url) self.assertIsNotNone(application) def test_get(self): self.client.get.return_value = self.mock_response( - '/v2/apps/app_id', - HTTPStatus.OK, - None, - 'v2', 'apps', 'GET_{id}_response.json') - application = self.client.v2.apps.get('app_id') + "/v2/apps/app_id", HTTPStatus.OK, None, "v2", "apps", "GET_{id}_response.json" + ) + application = self.client.v2.apps.get("app_id") self.client.get.assert_called_with(self.client.get.return_value.url) self.assertIsNotNone(application) def test_start(self): self.client.put.return_value = self.mock_response( - '/v2/apps/app_id', - HTTPStatus.CREATED, - None, - 'v2', 'apps', 'PUT_{id}_response.json') + "/v2/apps/app_id", HTTPStatus.CREATED, None, "v2", "apps", "PUT_{id}_response.json" + ) mock_summary = self.mock_response( - '/v2/apps/app_id/summary', - HTTPStatus.OK, - None, - 'v2', 'apps', 'GET_{id}_summary_response.json') + "/v2/apps/app_id/summary", HTTPStatus.OK, None, "v2", "apps", "GET_{id}_summary_response.json" + ) mock_instances_stopped = self.mock_response( - '/v2/apps/app_id/instances', - HTTPStatus.BAD_REQUEST, - None, - 'v2', 'apps', 'GET_{id}_instances_stopped_response.json') + "/v2/apps/app_id/instances", HTTPStatus.BAD_REQUEST, None, "v2", "apps", "GET_{id}_instances_stopped_response.json" + ) mock_instances_started = self.mock_response( - '/v2/apps/app_id/instances', - HTTPStatus.OK, - None, - 'v2', 'apps', 'GET_{id}_instances_response.json') - self.client.get.side_effect = [mock_summary, - InvalidStatusCode(HTTPStatus.BAD_REQUEST, dict(code=220001)), - mock_instances_started] + "/v2/apps/app_id/instances", HTTPStatus.OK, None, "v2", "apps", "GET_{id}_instances_response.json" + ) + self.client.get.side_effect = [ + mock_summary, + InvalidStatusCode(HTTPStatus.BAD_REQUEST, dict(code=220001)), + mock_instances_started, + ] - application = self.client.v2.apps.start('app_id') - self.client.put.assert_called_with(self.client.put.return_value.url, - json=dict(state='STARTED')) - self.client.get.assert_has_calls([call(mock_summary.url), - call(mock_instances_stopped.url), - call(mock_instances_started.url)], - any_order=False) + application = self.client.v2.apps.start("app_id") + self.client.put.assert_called_with(self.client.put.return_value.url, json=dict(state="STARTED")) + self.client.get.assert_has_calls( + [call(mock_summary.url), call(mock_instances_stopped.url), call(mock_instances_started.url)], any_order=False + ) self.assertIsNotNone(application) def test_stop(self): self.client.put.return_value = self.mock_response( - '/v2/apps/app_id', - HTTPStatus.CREATED, - None, - 'v2', 'apps', 'PUT_{id}_response.json') + "/v2/apps/app_id", HTTPStatus.CREATED, None, "v2", "apps", "PUT_{id}_response.json" + ) self.client.get.side_effect = [InvalidStatusCode(HTTPStatus.BAD_REQUEST, dict(code=220001))] - application = self.client.v2.apps.stop('app_id') - self.client.put.assert_called_with(self.client.put.return_value.url, - json=dict(state='STOPPED')) - self.client.get.assert_called_with('%s/v2/apps/app_id/instances' % self.TARGET_ENDPOINT) + application = self.client.v2.apps.stop("app_id") + self.client.put.assert_called_with(self.client.put.return_value.url, json=dict(state="STOPPED")) + self.client.get.assert_called_with("%s/v2/apps/app_id/instances" % self.TARGET_ENDPOINT) self.assertIsNotNone(application) def test_restart_instance(self): - self.client.delete.return_value = self.mock_response( - '/v2/apps/app_id/instances/666', - HTTPStatus.NO_CONTENT, None) - self.client.v2.apps.restart_instance('app_id', 666) + self.client.delete.return_value = self.mock_response("/v2/apps/app_id/instances/666", HTTPStatus.NO_CONTENT, None) + self.client.v2.apps.restart_instance("app_id", 666) self.client.delete.assert_called_with(self.client.delete.return_value.url) def test_create(self): self.client.post.return_value = self.mock_response( - '/v2/apps', - HTTPStatus.CREATED, - None, - 'v2', 'apps', 'POST_response.json') - application = self.client.v2.apps.create(name='test', space_guid='1fbb3e81-4f55-4fd3-9820-45febbd5e53e', - stack_guid='82f9c01c-72f2-4d3e-b5ed-eab97a6203cf', memory=1024, - instances=1, - disk_quota=1024, health_check_type="port") - self.client.post.assert_called_with(self.client.post.return_value.url, - json=dict(name='test', space_guid='1fbb3e81-4f55-4fd3-9820-45febbd5e53e', - stack_guid='82f9c01c-72f2-4d3e-b5ed-eab97a6203cf', memory=1024, - instances=1, disk_quota=1024, health_check_type="port")) + "/v2/apps", HTTPStatus.CREATED, None, "v2", "apps", "POST_response.json" + ) + application = self.client.v2.apps.create( + name="test", + space_guid="1fbb3e81-4f55-4fd3-9820-45febbd5e53e", + stack_guid="82f9c01c-72f2-4d3e-b5ed-eab97a6203cf", + memory=1024, + instances=1, + disk_quota=1024, + health_check_type="port", + ) + self.client.post.assert_called_with( + self.client.post.return_value.url, + json=dict( + name="test", + space_guid="1fbb3e81-4f55-4fd3-9820-45febbd5e53e", + stack_guid="82f9c01c-72f2-4d3e-b5ed-eab97a6203cf", + memory=1024, + instances=1, + disk_quota=1024, + health_check_type="port", + ), + ) self.assertIsNotNone(application) def test_update(self): self.client.put.return_value = self.mock_response( - '/v2/apps/app_id', - HTTPStatus.CREATED, - None, - 'v2', 'apps', 'PUT_{id}_response.json') - application = self.client.v2.apps.update('app_id', stack_guid='82f9c01c-72f2-4d3e-b5ed-eab97a6203cf', - memory=1024, - instances=1, disk_quota=1024, health_check_type="port") - self.client.put.assert_called_with(self.client.put.return_value.url, - json=dict(stack_guid='82f9c01c-72f2-4d3e-b5ed-eab97a6203cf', memory=1024, - instances=1, disk_quota=1024, health_check_type="port")) + "/v2/apps/app_id", HTTPStatus.CREATED, None, "v2", "apps", "PUT_{id}_response.json" + ) + application = self.client.v2.apps.update( + "app_id", + stack_guid="82f9c01c-72f2-4d3e-b5ed-eab97a6203cf", + memory=1024, + instances=1, + disk_quota=1024, + health_check_type="port", + ) + self.client.put.assert_called_with( + self.client.put.return_value.url, + json=dict( + stack_guid="82f9c01c-72f2-4d3e-b5ed-eab97a6203cf", + memory=1024, + instances=1, + disk_quota=1024, + health_check_type="port", + ), + ) self.assertIsNotNone(application) def test_remove(self): - self.client.delete.return_value = self.mock_response( - '/v2/apps/app_id', - HTTPStatus.NO_CONTENT, None) - self.client.v2.apps.remove('app_id') + self.client.delete.return_value = self.mock_response("/v2/apps/app_id", HTTPStatus.NO_CONTENT, None) + self.client.v2.apps.remove("app_id") self.client.delete.assert_called_with(self.client.delete.return_value.url) def test_restage(self): self.client.post.return_value = self.mock_response( - '/v2/apps/app_id/restage', - HTTPStatus.CREATED, - None, - 'v2', 'apps', 'POST_{id}_restage_response.json') - self.client.v2.apps.restage('app_id') + "/v2/apps/app_id/restage", HTTPStatus.CREATED, None, "v2", "apps", "POST_{id}_restage_response.json" + ) + self.client.v2.apps.restage("app_id") self.client.post.assert_called_with(self.client.post.return_value.url, json=None) def test_entity(self): self.client.get.side_effect = [ - self.mock_response( - '/v2/apps/app_id', - HTTPStatus.OK, - None, - 'v2', 'apps', 'GET_{id}_response.json'), - self.mock_response( - '/v2/spaces/space_id', - HTTPStatus.OK, - None, - 'v2', 'spaces', 'GET_{id}_response.json') - , - self.mock_response( - '/v2/routes', - HTTPStatus.OK, - None, - 'v2', 'routes', 'GET_response.json') + self.mock_response("/v2/apps/app_id", HTTPStatus.OK, None, "v2", "apps", "GET_{id}_response.json"), + self.mock_response("/v2/spaces/space_id", HTTPStatus.OK, None, "v2", "spaces", "GET_{id}_response.json"), + self.mock_response("/v2/routes", HTTPStatus.OK, None, "v2", "routes", "GET_response.json"), ] - application = self.client.v2.apps.get('app_id') + application = self.client.v2.apps.get("app_id") self.assertIsNotNone(application.space()) cpt = reduce(lambda increment, _: increment + 1, application.routes(), 0) self.assertEqual(cpt, 1) - self.client.get.assert_has_calls([call(side_effect.url) for side_effect in self.client.get.side_effect], - any_order=False) + self.client.get.assert_has_calls([call(side_effect.url) for side_effect in self.client.get.side_effect], any_order=False) - @patch.object(sys, 'argv', ['main', 'list_apps']) + @patch.object(sys, "argv", ["main", "list_apps"]) def test_main_list_apps(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = self.mock_response('/v2/apps', - HTTPStatus.OK, - None, - 'v2', 'apps', 'GET_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.get.return_value = self.mock_response("/v2/apps", HTTPStatus.OK, None, "v2", "apps", "GET_response.json") main.main() self.client.get.assert_called_with(self.client.get.return_value.url) - @patch.object(sys, 'argv', ['main', 'delete_app', '906775ea-622e-4bc7-af5d-9aab3b652f81']) + @patch.object(sys, "argv", ["main", "delete_app", "906775ea-622e-4bc7-af5d-9aab3b652f81"]) def test_main_delete_apps(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.delete.return_value = self.mock_response('/v2/apps/906775ea-622e-4bc7-af5d-9aab3b652f81', - HTTPStatus.NO_CONTENT, - None) + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.delete.return_value = self.mock_response( + "/v2/apps/906775ea-622e-4bc7-af5d-9aab3b652f81", HTTPStatus.NO_CONTENT, None + ) main.main() self.client.delete.assert_called_with(self.client.delete.return_value.url) - @patch.object(sys, 'argv', ['main', 'get_app', '906775ea-622e-4bc7-af5d-9aab3b652f81']) + @patch.object(sys, "argv", ["main", "get_app", "906775ea-622e-4bc7-af5d-9aab3b652f81"]) def test_main_get_app(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = self.mock_response('/v2/apps/906775ea-622e-4bc7-af5d-9aab3b652f81', - HTTPStatus.OK, - None, - 'v2', 'apps', 'GET_{id}_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.get.return_value = self.mock_response( + "/v2/apps/906775ea-622e-4bc7-af5d-9aab3b652f81", HTTPStatus.OK, None, "v2", "apps", "GET_{id}_response.json" + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) - @patch.object(sys, 'argv', ['main', 'restage', '906775ea-622e-4bc7-af5d-9aab3b652f81']) + @patch.object(sys, "argv", ["main", "restage", "906775ea-622e-4bc7-af5d-9aab3b652f81"]) def test_main_restage_app(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.post.return_value = self.mock_response('/v2/apps/906775ea-622e-4bc7-af5d-9aab3b652f81/restage', - HTTPStatus.CREATED, - None, - 'v2', 'apps', 'POST_{id}_restage_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.post.return_value = self.mock_response( + "/v2/apps/906775ea-622e-4bc7-af5d-9aab3b652f81/restage", + HTTPStatus.CREATED, + None, + "v2", + "apps", + "POST_{id}_restage_response.json", + ) main.main() self.client.post.assert_called_with(self.client.post.return_value.url, json=None) - @patch.object(sys, 'argv', ['main', 'restart_instance', '906775ea-622e-4bc7-af5d-9aab3b652f81', '666']) + @patch.object(sys, "argv", ["main", "restart_instance", "906775ea-622e-4bc7-af5d-9aab3b652f81", "666"]) def test_main_restart_app_instance(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): self.client.delete.return_value = self.mock_response( - '/v2/apps/906775ea-622e-4bc7-af5d-9aab3b652f81/instances/666', - HTTPStatus.NO_CONTENT, - None) + "/v2/apps/906775ea-622e-4bc7-af5d-9aab3b652f81/instances/666", HTTPStatus.NO_CONTENT, None + ) main.main() self.client.delete.assert_called_with(self.client.delete.return_value.url) diff --git a/test/v2/test_buildpacks.py b/test/v2/test_buildpacks.py index 7ea4032..e37b6d4 100644 --- a/test/v2/test_buildpacks.py +++ b/test/v2/test_buildpacks.py @@ -14,31 +14,25 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = self.mock_response('/v2/buildpacks', - HTTPStatus.OK, - None, - 'v2', 'buildpacks', 'GET_response.json') + self.client.get.return_value = self.mock_response( + "/v2/buildpacks", HTTPStatus.OK, None, "v2", "buildpacks", "GET_response.json" + ) cpt = reduce(lambda increment, _: increment + 1, self.client.v2.buildpacks.list(), 0) self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(cpt, 3) def test_get(self): self.client.get.return_value = self.mock_response( - '/v2/buildpacks/buildpack_id', - HTTPStatus.OK, - None, - 'v2', 'buildpacks', 'GET_{id}_response.json') - result = self.client.v2.buildpacks.get('buildpack_id') + "/v2/buildpacks/buildpack_id", HTTPStatus.OK, None, "v2", "buildpacks", "GET_{id}_response.json" + ) + result = self.client.v2.buildpacks.get("buildpack_id") self.client.get.assert_called_with(self.client.get.return_value.url) self.assertIsNotNone(result) def test_update(self): self.client.put.return_value = self.mock_response( - '/v2/buildpacks/build_pack_id', - HTTPStatus.CREATED, - None, - 'v2', 'apps', 'PUT_{id}_response.json') - result = self.client.v2.buildpacks.update('build_pack_id', dict(enabled=False)) - self.client.put.assert_called_with(self.client.put.return_value.url, - json=dict(enabled=False)) + "/v2/buildpacks/build_pack_id", HTTPStatus.CREATED, None, "v2", "apps", "PUT_{id}_response.json" + ) + result = self.client.v2.buildpacks.update("build_pack_id", dict(enabled=False)) + self.client.put.assert_called_with(self.client.put.return_value.url, json=dict(enabled=False)) self.assertIsNotNone(result) diff --git a/test/v2/test_doppler.py b/test/v2/test_doppler.py index 3c2e88f..02ec451 100644 --- a/test/v2/test_doppler.py +++ b/test/v2/test_doppler.py @@ -14,19 +14,20 @@ def setUpClass(cls): def setUp(self): self.build_client() - self.doppler = DopplerClient(re.sub('^http', 'ws', self.TARGET_ENDPOINT), - proxy='', - verify_ssl=False, - credentials_manager=self.client) + self.doppler = DopplerClient( + re.sub("^http", "ws", self.TARGET_ENDPOINT), proxy="", verify_ssl=False, credentials_manager=self.client + ) def test_recents(self): - boundary = 'd661b2c1426a3abcf1c0524d7fdbc774c42a767bdd6702141702d16047bc' - app_guid = 'app_id' - self.client.get.return_value = self.mock_response('/apps/%s/recentlogs' % app_guid, - HTTPStatus.OK, - {'content-type': - 'multipart/x-protobuf; boundary=%s' % boundary}, - 'recents', 'GET_response.bin') + boundary = "d661b2c1426a3abcf1c0524d7fdbc774c42a767bdd6702141702d16047bc" + app_guid = "app_id" + self.client.get.return_value = self.mock_response( + "/apps/%s/recentlogs" % app_guid, + HTTPStatus.OK, + {"content-type": "multipart/x-protobuf; boundary=%s" % boundary}, + "recents", + "GET_response.bin", + ) cpt = reduce(lambda increment, _: increment + 1, self.doppler.recent_logs(app_guid), 0) self.client.get.assert_called_with(self.client.get.return_value.url, stream=True) self.assertEqual(cpt, 200) diff --git a/test/v2/test_entities.py b/test/v2/test_entities.py index 9494436..d08c2a6 100644 --- a/test/v2/test_entities.py +++ b/test/v2/test_entities.py @@ -9,108 +9,95 @@ class TestEntities(unittest.TestCase, AbstractTestCase): - def test_invalid_entity_without_entity_attribute(self): client = MagicMock() - entity_manager = EntityManager(self.TARGET_ENDPOINT, client, '/fake/anyone') + entity_manager = EntityManager(self.TARGET_ENDPOINT, client, "/fake/anyone") client.get.return_value = self.mock_response( - '/fake/anyone/any-id', - HTTPStatus.OK, - None, - 'fake', 'GET_invalid_entity_without_entity.json') + "/fake/anyone/any-id", HTTPStatus.OK, None, "fake", "GET_invalid_entity_without_entity.json" + ) - self.assertRaises(InvalidEntity, lambda: entity_manager['any-id']) + self.assertRaises(InvalidEntity, lambda: entity_manager["any-id"]) def test_invalid_entity_with_null_entity(self): client = MagicMock() - entity_manager = EntityManager(self.TARGET_ENDPOINT, client, '/fake/anyone') + entity_manager = EntityManager(self.TARGET_ENDPOINT, client, "/fake/anyone") client.get.return_value = self.mock_response( - '/fake/anyone/any-id', - HTTPStatus.OK, - None, - 'fake', 'GET_invalid_entity_with_null_entity.json') + "/fake/anyone/any-id", HTTPStatus.OK, None, "fake", "GET_invalid_entity_with_null_entity.json" + ) - self.assertRaises(InvalidEntity, lambda: entity_manager['any-id']) + self.assertRaises(InvalidEntity, lambda: entity_manager["any-id"]) def test_invalid_entity_with_invalid_entity_type(self): client = MagicMock() - entity_manager = EntityManager(self.TARGET_ENDPOINT, client, '/fake/anyone') + entity_manager = EntityManager(self.TARGET_ENDPOINT, client, "/fake/anyone") client.get.return_value = self.mock_response( - '/fake/anyone/any-id', - HTTPStatus.OK, - None, - 'fake', 'GET_invalid_entity_with_invalid_entity_type.json') + "/fake/anyone/any-id", HTTPStatus.OK, None, "fake", "GET_invalid_entity_with_invalid_entity_type.json" + ) - self.assertRaises(InvalidEntity, lambda: entity_manager['any-id']) + self.assertRaises(InvalidEntity, lambda: entity_manager["any-id"]) def test_query(self): - url = EntityManager('http://cf.api', None, '/v2/apps')._get_url_filtered('/v2/apps', **{'results-per-page': 20, - 'order-direction': 'asc', - 'page': 1, - 'space_guid': 'id', - 'order-by': 'id'}) - self.assertEqual('/v2/apps?order-by=id&order-direction=asc&page=1&results-per-page=20&q=space_guid%3Aid', url) + url = EntityManager("http://cf.api", None, "/v2/apps")._get_url_filtered( + "/v2/apps", **{"results-per-page": 20, "order-direction": "asc", "page": 1, "space_guid": "id", "order-by": "id"} + ) + self.assertEqual("/v2/apps?order-by=id&order-direction=asc&page=1&results-per-page=20&q=space_guid%3Aid", url) def test_query_multi_order_by(self): - url = EntityManager('http://cf.api', None, '/v2/apps')._get_url_filtered('/v2/apps', - **{'order-by': ['timestamp', 'id']}) - self.assertEqual('/v2/apps?order-by=timestamp&order-by=id', url) + url = EntityManager("http://cf.api", None, "/v2/apps")._get_url_filtered("/v2/apps", **{"order-by": ["timestamp", "id"]}) + self.assertEqual("/v2/apps?order-by=timestamp&order-by=id", url) def test_query_single_order_by(self): - url = EntityManager('http://cf.api', None, '/v2/apps')._get_url_filtered('/v2/apps', - **{'order-by': 'timestamp'}) - self.assertEqual('/v2/apps?order-by=timestamp', url) + url = EntityManager("http://cf.api", None, "/v2/apps")._get_url_filtered("/v2/apps", **{"order-by": "timestamp"}) + self.assertEqual("/v2/apps?order-by=timestamp", url) def test_query_in(self): - url = EntityManager('http://cf.api', None, '/v2/apps')._get_url_filtered('/v2/apps', **{'results-per-page': 20, - 'order-direction': 'asc', - 'page': 1, - 'space_guid': ['id1', - 'id2']}) - self.assertEqual('/v2/apps?order-direction=asc&page=1&results-per-page=20&q=space_guid%20IN%20id1%2Cid2', url) + url = EntityManager("http://cf.api", None, "/v2/apps")._get_url_filtered( + "/v2/apps", **{"results-per-page": 20, "order-direction": "asc", "page": 1, "space_guid": ["id1", "id2"]} + ) + self.assertEqual("/v2/apps?order-direction=asc&page=1&results-per-page=20&q=space_guid%20IN%20id1%2Cid2", url) def test_multi_query(self): - url = EntityManager('http://cf.api', None, '/v2/events')._get_url_filtered('/v2/events', - **{'type': ['create', 'update'], - 'organization_guid': 'org-id'}) - self.assertEqual('/v2/events?q=organization_guid%3Aorg-id&q=type%20IN%20create%2Cupdate', url) + url = EntityManager("http://cf.api", None, "/v2/events")._get_url_filtered( + "/v2/events", **{"type": ["create", "update"], "organization_guid": "org-id"} + ) + self.assertEqual("/v2/events?q=organization_guid%3Aorg-id&q=type%20IN%20create%2Cupdate", url) def test_list(self): client = MagicMock() - entity_manager = EntityManager(self.TARGET_ENDPOINT, client, '/fake/first') + entity_manager = EntityManager(self.TARGET_ENDPOINT, client, "/fake/first") first_response = self.mock_response( - '/fake/first?order-direction=asc&page=1&results-per-page=20&q=space_guid%3Asome-id', + "/fake/first?order-direction=asc&page=1&results-per-page=20&q=space_guid%3Asome-id", HTTPStatus.OK, None, - 'fake', 'GET_multi_page_0_response.json') - second_response = self.mock_response('/fake/next?order-direction=asc&page=2&results-per-page=50', - HTTPStatus.OK, - None, - 'fake', 'GET_multi_page_1_response.json') + "fake", + "GET_multi_page_0_response.json", + ) + second_response = self.mock_response( + "/fake/next?order-direction=asc&page=2&results-per-page=50", + HTTPStatus.OK, + None, + "fake", + "GET_multi_page_1_response.json", + ) client.get.side_effect = [first_response, second_response] - cpt = reduce(lambda increment, _: increment + 1, entity_manager.list(**{"results-per-page": 20, - 'order-direction': 'asc', - 'page': 1, - "space_guid": 'some-id'}), 0) - client.get.assert_has_calls([call(first_response.url), - call(second_response.url)], - any_order=False) + cpt = reduce( + lambda increment, _: increment + 1, + entity_manager.list(**{"results-per-page": 20, "order-direction": "asc", "page": 1, "space_guid": "some-id"}), + 0, + ) + client.get.assert_has_calls([call(first_response.url), call(second_response.url)], any_order=False) self.assertEqual(cpt, 3) def test_iter(self): client = MagicMock() - entity_manager = EntityManager(self.TARGET_ENDPOINT, client, '/fake/something') + entity_manager = EntityManager(self.TARGET_ENDPOINT, client, "/fake/something") - client.get.return_value = self.mock_response( - '/fake/something', - HTTPStatus.OK, - None, - 'fake', 'GET_response.json') + client.get.return_value = self.mock_response("/fake/something", HTTPStatus.OK, None, "fake", "GET_response.json") cpt = reduce(lambda increment, _: increment + 1, entity_manager, 0) client.get.assert_called_with(client.get.return_value.url) @@ -118,14 +105,12 @@ def test_iter(self): def test_get_elem(self): client = MagicMock() - entity_manager = EntityManager(self.TARGET_ENDPOINT, client, '/fake/something') + entity_manager = EntityManager(self.TARGET_ENDPOINT, client, "/fake/something") client.get.return_value = self.mock_response( - '/fake/something/with-id', - HTTPStatus.OK, - None, - 'fake', 'GET_{id}_response.json') - entity = entity_manager['with-id'] + "/fake/something/with-id", HTTPStatus.OK, None, "fake", "GET_{id}_response.json" + ) + entity = entity_manager["with-id"] client.get.assert_called_with(client.get.return_value.url) - self.assertEqual(entity['entity']['name'], 'name-423') + self.assertEqual(entity["entity"]["name"], "name-423") diff --git a/test/v2/test_events.py b/test/v2/test_events.py index 8d61889..e9764fc 100644 --- a/test/v2/test_events.py +++ b/test/v2/test_events.py @@ -13,13 +13,16 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = self.mock_response('/v2/events?q=type%3Aaudit.route.delete-request', - HTTPStatus.OK, - None, - 'v2', 'events', - 'GET_response_audit.route.delete-request.json') - delete_route_events = [event for event in self.client.v2.event.list_by_type('audit.route.delete-request')] + self.client.get.return_value = self.mock_response( + "/v2/events?q=type%3Aaudit.route.delete-request", + HTTPStatus.OK, + None, + "v2", + "events", + "GET_response_audit.route.delete-request.json", + ) + delete_route_events = [event for event in self.client.v2.event.list_by_type("audit.route.delete-request")] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(len(delete_route_events), 1) - print('test_list - Event - %s' % str(delete_route_events[0])) - self.assertEqual(delete_route_events[0]['entity']['type'], "audit.route.delete-request") + print("test_list - Event - %s" % str(delete_route_events[0])) + self.assertEqual(delete_route_events[0]["entity"]["type"], "audit.route.delete-request") diff --git a/test/v2/test_organizations.py b/test/v2/test_organizations.py index efd8714..b7f5ed9 100644 --- a/test/v2/test_organizations.py +++ b/test/v2/test_organizations.py @@ -15,39 +15,34 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = self.mock_response('/v2/organizations?q=name%3Aorganization_name', - HTTPStatus.OK, - None, - 'v2', 'organizations', 'GET_response.json') - cpt = reduce(lambda increment, _: increment + 1, self.client.v2.organizations.list(name='organization_name'), 0) + self.client.get.return_value = self.mock_response( + "/v2/organizations?q=name%3Aorganization_name", HTTPStatus.OK, None, "v2", "organizations", "GET_response.json" + ) + cpt = reduce(lambda increment, _: increment + 1, self.client.v2.organizations.list(name="organization_name"), 0) self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(cpt, 1) def test_get(self): self.client.get.return_value = self.mock_response( - '/v2/organizations/org_id', - HTTPStatus.OK, - None, - 'v2', 'organizations', 'GET_{id}_response.json') - result = self.client.v2.organizations.get('org_id') + "/v2/organizations/org_id", HTTPStatus.OK, None, "v2", "organizations", "GET_{id}_response.json" + ) + result = self.client.v2.organizations.get("org_id") self.client.get.assert_called_with(self.client.get.return_value.url) self.assertIsNotNone(result) def test_entity(self): self.client.get.side_effect = [ + self.mock_response("/v2/organizations/org_id", HTTPStatus.OK, None, "v2", "organizations", "GET_{id}_response.json"), self.mock_response( - '/v2/organizations/org_id', - HTTPStatus.OK, - None, - 'v2', 'organizations', 'GET_{id}_response.json'), - self.mock_response( - '/v2/organizations/fe79371b-39b8-4f0d-8331-cff423a06aca/spaces', + "/v2/organizations/fe79371b-39b8-4f0d-8331-cff423a06aca/spaces", HTTPStatus.OK, None, - 'v2', 'spaces', 'GET_response.json') + "v2", + "spaces", + "GET_response.json", + ), ] - organization = self.client.v2.organizations.get('org_id') + organization = self.client.v2.organizations.get("org_id") cpt = reduce(lambda increment, _: increment + 1, organization.spaces(), 0) self.assertEqual(cpt, 1) - self.client.get.assert_has_calls([call(side_effect.url) for side_effect in self.client.get.side_effect], - any_order=False) + self.client.get.assert_has_calls([call(side_effect.url) for side_effect in self.client.get.side_effect], any_order=False) diff --git a/test/v2/test_routes.py b/test/v2/test_routes.py index 9b4706d..040df21 100644 --- a/test/v2/test_routes.py +++ b/test/v2/test_routes.py @@ -18,74 +18,59 @@ def setUp(self): def test_list(self): self.client.get.return_value = self.mock_response( - '/v2/routes?q=organization_guid%3Aorganization_guid', - HTTPStatus.OK, - None, - 'v2', 'routes', 'GET_response.json') - cpt = reduce(lambda increment, _: increment + 1, - self.client.v2.routes.list(organization_guid='organization_guid'), - 0) + "/v2/routes?q=organization_guid%3Aorganization_guid", HTTPStatus.OK, None, "v2", "routes", "GET_response.json" + ) + cpt = reduce(lambda increment, _: increment + 1, self.client.v2.routes.list(organization_guid="organization_guid"), 0) self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(cpt, 1) def test_get(self): self.client.get.return_value = self.mock_response( - '/v2/routes/route_id', - HTTPStatus.OK, - None, - 'v2', 'routes', 'GET_{id}_response.json') - result = self.client.v2.routes.get('route_id') + "/v2/routes/route_id", HTTPStatus.OK, None, "v2", "routes", "GET_{id}_response.json" + ) + result = self.client.v2.routes.get("route_id") self.client.get.assert_called_with(self.client.get.return_value.url) self.assertIsNotNone(result) def test_entity(self): self.client.get.side_effect = [ + self.mock_response("/v2/routes/route_id", HTTPStatus.OK, None, "v2", "routes", "GET_{id}_response.json"), self.mock_response( - '/v2/routes/route_id', + "/v2/service_instances/e3db4ea8-ab0c-4c47-adf8-a70a8e990ee4", HTTPStatus.OK, None, - 'v2', 'routes', 'GET_{id}_response.json'), + "v2", + "service_instances", + "GET_{id}_response.json", + ), self.mock_response( - '/v2/service_instances/e3db4ea8-ab0c-4c47-adf8-a70a8e990ee4', - HTTPStatus.OK, - None, - 'v2', 'service_instances', 'GET_{id}_response.json'), + "/v2/spaces/b3f94ab9-1520-478b-a6d6-eb467c179ada", HTTPStatus.OK, None, "v2", "spaces", "GET_{id}_response.json" + ), self.mock_response( - '/v2/spaces/b3f94ab9-1520-478b-a6d6-eb467c179ada', - HTTPStatus.OK, - None, - 'v2', 'spaces', 'GET_{id}_response.json'), - self.mock_response('/v2/routes/75c16cfe-9b8a-4faf-bb65-02c713c7956f/apps', - HTTPStatus.OK, - None, - 'v2', 'apps', 'GET_response.json') + "/v2/routes/75c16cfe-9b8a-4faf-bb65-02c713c7956f/apps", HTTPStatus.OK, None, "v2", "apps", "GET_response.json" + ), ] - route = self.client.v2.routes.get('route_id') + route = self.client.v2.routes.get("route_id") self.assertIsNotNone(route.service_instance()) self.assertIsNotNone(route.space()) cpt = reduce(lambda increment, _: increment + 1, route.apps(), 0) self.assertEqual(cpt, 3) - self.client.get.assert_has_calls([call(side_effect.url) for side_effect in self.client.get.side_effect], - any_order=False) + self.client.get.assert_has_calls([call(side_effect.url) for side_effect in self.client.get.side_effect], any_order=False) - @patch.object(sys, 'argv', ['main', 'list_routes']) + @patch.object(sys, "argv", ["main", "list_routes"]) def test_main_list_routes(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = self.mock_response('/v2/routes', - HTTPStatus.OK, - None, - 'v2', 'routes', 'GET_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.get.return_value = self.mock_response( + "/v2/routes", HTTPStatus.OK, None, "v2", "routes", "GET_response.json" + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) - @patch.object(sys, 'argv', ['main', 'get_route', '75c16cfe-9b8a-4faf-bb65-02c713c7956f']) + @patch.object(sys, "argv", ["main", "get_route", "75c16cfe-9b8a-4faf-bb65-02c713c7956f"]) def test_main_get_route(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = self.mock_response('/v2/routes/75c16cfe-9b8a-4faf-bb65-02c713c7956f', - HTTPStatus.OK, - None, - 'v2', 'routes', 'GET_{id}_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.get.return_value = self.mock_response( + "/v2/routes/75c16cfe-9b8a-4faf-bb65-02c713c7956f", HTTPStatus.OK, None, "v2", "routes", "GET_{id}_response.json" + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) diff --git a/test/v2/test_service_bindings.py b/test/v2/test_service_bindings.py index 79c63cd..4d3d21f 100644 --- a/test/v2/test_service_bindings.py +++ b/test/v2/test_service_bindings.py @@ -18,93 +18,91 @@ def setUp(self): def test_list(self): self.client.get.return_value = self.mock_response( - '/v2/service_bindings?q=service_instance_guid%3Ainstance_guid', + "/v2/service_bindings?q=service_instance_guid%3Ainstance_guid", HTTPStatus.OK, None, - 'v2', 'service_bindings', 'GET_response.json') - cpt = reduce(lambda increment, _: increment + 1, - self.client.v2.service_bindings.list(service_instance_guid='instance_guid'), 0) + "v2", + "service_bindings", + "GET_response.json", + ) + cpt = reduce( + lambda increment, _: increment + 1, self.client.v2.service_bindings.list(service_instance_guid="instance_guid"), 0 + ) self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(cpt, 1) def test_get(self): self.client.get.return_value = self.mock_response( - '/v2/service_bindings/service_binding_id', - HTTPStatus.OK, - None, - 'v2', 'service_bindings', 'GET_{id}_response.json') - result = self.client.v2.service_bindings.get('service_binding_id') + "/v2/service_bindings/service_binding_id", HTTPStatus.OK, None, "v2", "service_bindings", "GET_{id}_response.json" + ) + result = self.client.v2.service_bindings.get("service_binding_id") self.client.get.assert_called_with(self.client.get.return_value.url) self.assertIsNotNone(result) def test_create(self): self.client.post.return_value = self.mock_response( - '/v2/service_bindings', - HTTPStatus.CREATED, - None, - 'v2', 'service_bindings', 'POST_response.json') - service_binding = self.client.v2.service_bindings.create('app_guid', 'instance_guid', - dict(the_service_broker='wants this object'), - 'binding_name') - self.client.post.assert_called_with(self.client.post.return_value.url, - json=dict(app_guid='app_guid', - service_instance_guid='instance_guid', - name='binding_name', - parameters=dict( - the_service_broker='wants this object'))) + "/v2/service_bindings", HTTPStatus.CREATED, None, "v2", "service_bindings", "POST_response.json" + ) + service_binding = self.client.v2.service_bindings.create( + "app_guid", "instance_guid", dict(the_service_broker="wants this object"), "binding_name" + ) + self.client.post.assert_called_with( + self.client.post.return_value.url, + json=dict( + app_guid="app_guid", + service_instance_guid="instance_guid", + name="binding_name", + parameters=dict(the_service_broker="wants this object"), + ), + ) self.assertIsNotNone(service_binding) def test_delete(self): - self.client.delete.return_value = self.mock_response( - '/v2/service_bindings/binding_id', - HTTPStatus.NO_CONTENT, - None) - self.client.v2.service_bindings.remove('binding_id') + self.client.delete.return_value = self.mock_response("/v2/service_bindings/binding_id", HTTPStatus.NO_CONTENT, None) + self.client.v2.service_bindings.remove("binding_id") self.client.delete.assert_called_with(self.client.delete.return_value.url) def test_entity(self): self.client.get.side_effect = [ self.mock_response( - '/v2/service_bindings/service_binding_id', - HTTPStatus.OK, - None, - 'v2', 'service_bindings', 'GET_{id}_response.json'), + "/v2/service_bindings/service_binding_id", HTTPStatus.OK, None, "v2", "service_bindings", "GET_{id}_response.json" + ), self.mock_response( - '/v2/service_instances/ef0bf611-82c6-4603-99fc-3a1a893109d0', + "/v2/service_instances/ef0bf611-82c6-4603-99fc-3a1a893109d0", HTTPStatus.OK, None, - 'v2', 'service_instances', 'GET_{id}_response.json'), + "v2", + "service_instances", + "GET_{id}_response.json", + ), self.mock_response( - '/v2/apps/c77953c8-6c35-46c7-816e-cf0c42ac2f52', - HTTPStatus.OK, - None, - 'v2', 'apps', 'GET_{id}_response.json') + "/v2/apps/c77953c8-6c35-46c7-816e-cf0c42ac2f52", HTTPStatus.OK, None, "v2", "apps", "GET_{id}_response.json" + ), ] - service_binding = self.client.v2.service_bindings.get('service_binding_id') + service_binding = self.client.v2.service_bindings.get("service_binding_id") self.assertIsNotNone(service_binding.service_instance()) self.assertIsNotNone(service_binding.app()) - self.client.get.assert_has_calls([call(side_effect.url) for side_effect in self.client.get.side_effect], - any_order=False) + self.client.get.assert_has_calls([call(side_effect.url) for side_effect in self.client.get.side_effect], any_order=False) - @patch.object(sys, 'argv', ['main', 'list_service_bindings']) + @patch.object(sys, "argv", ["main", "list_service_bindings"]) def test_main_list_service_bindings(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = self.mock_response('/v2/service_bindings', - HTTPStatus.OK, - None, - 'v2', 'service_bindings', 'GET_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.get.return_value = self.mock_response( + "/v2/service_bindings", HTTPStatus.OK, None, "v2", "service_bindings", "GET_response.json" + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) - @patch.object(sys, 'argv', ['main', 'get_service_binding', 'eaabd042-8f5c-44a2-9580-1e114c36bdcb']) + @patch.object(sys, "argv", ["main", "get_service_binding", "eaabd042-8f5c-44a2-9580-1e114c36bdcb"]) def test_main_get_service_binding(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): self.client.get.return_value = self.mock_response( - '/v2/service_bindings/eaabd042-8f5c-44a2-9580-1e114c36bdcb', + "/v2/service_bindings/eaabd042-8f5c-44a2-9580-1e114c36bdcb", HTTPStatus.OK, None, - 'v2', 'service_bindings', 'GET_{id}_response.json') + "v2", + "service_bindings", + "GET_{id}_response.json", + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) diff --git a/test/v2/test_service_brokers.py b/test/v2/test_service_brokers.py index 23bb683..8402e20 100644 --- a/test/v2/test_service_brokers.py +++ b/test/v2/test_service_brokers.py @@ -18,82 +18,68 @@ def setUp(self): def test_list(self): self.client.get.return_value = self.mock_response( - '/v2/service_brokers?q=space_guid%3Aspace_guid', - HTTPStatus.OK, - None, - 'v2', 'service_bindings', 'GET_response.json') - cpt = reduce(lambda increment, _: increment + 1, - self.client.v2.service_brokers.list(space_guid='space_guid'), 0) + "/v2/service_brokers?q=space_guid%3Aspace_guid", HTTPStatus.OK, None, "v2", "service_bindings", "GET_response.json" + ) + cpt = reduce(lambda increment, _: increment + 1, self.client.v2.service_brokers.list(space_guid="space_guid"), 0) self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(cpt, 1) def test_get(self): self.client.get.return_value = self.mock_response( - '/v2/service_brokers/broker_id', - HTTPStatus.OK, - None, - 'v2', 'service_brokers', 'GET_{id}_response.json') - result = self.client.v2.service_brokers.get('broker_id') + "/v2/service_brokers/broker_id", HTTPStatus.OK, None, "v2", "service_brokers", "GET_{id}_response.json" + ) + result = self.client.v2.service_brokers.get("broker_id") self.client.get.assert_called_with(self.client.get.return_value.url) self.assertIsNotNone(result) def test_create(self): self.client.post.return_value = self.mock_response( - '/v2/service_brokers', - HTTPStatus.CREATED, - None, - 'v2', 'service_brokers', 'POST_response.json') - service_broker = self.client.v2.service_brokers.create('url', 'name', 'username', 'P@sswd1') - self.client.post.assert_called_with(self.client.post.return_value.url, - json=dict(broker_url='url', - name='name', - auth_username='username', - auth_password='P@sswd1')) + "/v2/service_brokers", HTTPStatus.CREATED, None, "v2", "service_brokers", "POST_response.json" + ) + service_broker = self.client.v2.service_brokers.create("url", "name", "username", "P@sswd1") + self.client.post.assert_called_with( + self.client.post.return_value.url, + json=dict(broker_url="url", name="name", auth_username="username", auth_password="P@sswd1"), + ) self.assertIsNotNone(service_broker) def test_update(self): self.client.put.return_value = self.mock_response( - '/v2/service_brokers/broker_id', - HTTPStatus.OK, - None, - 'v2', 'service_brokers', 'PUT_{id}_response.json') - service_broker = self.client.v2.service_brokers.update('broker_id', - broker_url='new-url', - auth_username='new-username', - auth_password='P@sswd2') - self.client.put.assert_called_with(self.client.put.return_value.url, - json=dict(broker_url='new-url', - auth_username='new-username', - auth_password='P@sswd2')) + "/v2/service_brokers/broker_id", HTTPStatus.OK, None, "v2", "service_brokers", "PUT_{id}_response.json" + ) + service_broker = self.client.v2.service_brokers.update( + "broker_id", broker_url="new-url", auth_username="new-username", auth_password="P@sswd2" + ) + self.client.put.assert_called_with( + self.client.put.return_value.url, + json=dict(broker_url="new-url", auth_username="new-username", auth_password="P@sswd2"), + ) self.assertIsNotNone(service_broker) def test_delete(self): - self.client.delete.return_value = self.mock_response( - '/v2/service_brokers/broker_id', - HTTPStatus.NO_CONTENT, - None) - self.client.v2.service_brokers.remove('broker_id') + self.client.delete.return_value = self.mock_response("/v2/service_brokers/broker_id", HTTPStatus.NO_CONTENT, None) + self.client.v2.service_brokers.remove("broker_id") self.client.delete.assert_called_with(self.client.delete.return_value.url) - @patch.object(sys, 'argv', ['main', 'list_service_brokers']) + @patch.object(sys, "argv", ["main", "list_service_brokers"]) def test_main_list_service_brokers(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = self.mock_response('/v2/service_brokers', - HTTPStatus.OK, - None, - 'v2', 'service_brokers', 'GET_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.get.return_value = self.mock_response( + "/v2/service_brokers", HTTPStatus.OK, None, "v2", "service_brokers", "GET_response.json" + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) - @patch.object(sys, 'argv', ['main', 'get_service_broker', 'ade9730c-4ee5-4290-ad37-0b15cecd2ca6']) + @patch.object(sys, "argv", ["main", "get_service_broker", "ade9730c-4ee5-4290-ad37-0b15cecd2ca6"]) def test_main_get_service_broker(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): self.client.get.return_value = self.mock_response( - '/v2/service_brokers/ade9730c-4ee5-4290-ad37-0b15cecd2ca6', + "/v2/service_brokers/ade9730c-4ee5-4290-ad37-0b15cecd2ca6", HTTPStatus.OK, None, - 'v2', 'service_brokers', 'GET_{id}_response.json') + "v2", + "service_brokers", + "GET_{id}_response.json", + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) diff --git a/test/v2/test_service_instances.py b/test/v2/test_service_instances.py index 870b1d4..74f309b 100644 --- a/test/v2/test_service_instances.py +++ b/test/v2/test_service_instances.py @@ -18,161 +18,149 @@ def setUp(self): def test_list(self): self.client.get.return_value = self.mock_response( - '/v2/service_instances?q=service_plan_guid%3Aplan_id&q=space_guid%3Aspace_guid', + "/v2/service_instances?q=service_plan_guid%3Aplan_id&q=space_guid%3Aspace_guid", HTTPStatus.OK, None, - 'v2', 'service_instances', 'GET_response.json') - cpt = reduce(lambda increment, _: increment + 1, - self.client.v2.service_instances.list(space_guid='space_guid', service_plan_guid='plan_id'), 0) + "v2", + "service_instances", + "GET_response.json", + ) + cpt = reduce( + lambda increment, _: increment + 1, + self.client.v2.service_instances.list(space_guid="space_guid", service_plan_guid="plan_id"), + 0, + ) self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(cpt, 1) def test_get(self): self.client.get.return_value = self.mock_response( - '/v2/service_instances/instance_id', - HTTPStatus.OK, - None, - 'v2', 'service_instances', 'GET_{id}_response.json') - result = self.client.v2.service_instances.get('instance_id') + "/v2/service_instances/instance_id", HTTPStatus.OK, None, "v2", "service_instances", "GET_{id}_response.json" + ) + result = self.client.v2.service_instances.get("instance_id") self.client.get.assert_called_with(self.client.get.return_value.url) self.assertIsNotNone(result) def test_create(self): self.client.post.return_value = self.mock_response( - '/v2/service_instances', - HTTPStatus.CREATED, - None, - 'v2', 'service_instances', 'POST_response.json') - service_instance = self.client.v2.service_instances.create('space_guid', 'name', 'plan_id', - parameters=dict( - the_service_broker="wants this object"), - tags=['mongodb'], - accepts_incomplete=True) - self.client.post.assert_called_with(self.client.post.return_value.url, - json=dict(name='name', - space_guid='space_guid', - service_plan_guid='plan_id', - parameters=dict( - the_service_broker="wants this object"), - tags=['mongodb']), - params=dict(accepts_incomplete="true") - ) + "/v2/service_instances", HTTPStatus.CREATED, None, "v2", "service_instances", "POST_response.json" + ) + service_instance = self.client.v2.service_instances.create( + "space_guid", + "name", + "plan_id", + parameters=dict(the_service_broker="wants this object"), + tags=["mongodb"], + accepts_incomplete=True, + ) + self.client.post.assert_called_with( + self.client.post.return_value.url, + json=dict( + name="name", + space_guid="space_guid", + service_plan_guid="plan_id", + parameters=dict(the_service_broker="wants this object"), + tags=["mongodb"], + ), + params=dict(accepts_incomplete="true"), + ) self.assertIsNotNone(service_instance) def test_update(self): self.client.put.return_value = self.mock_response( - '/v2/service_instances/instance_id', - HTTPStatus.OK, - None, - 'v2', 'service_instances', 'PUT_{id}_response.json') - service_instance = self.client.v2.service_instances.update('instance_id', instance_name='new-name', - tags=['other-tag']) - self.client.put.assert_called_with(self.client.put.return_value.url, - json=dict(name='new-name', - tags=['other-tag']), params=None) + "/v2/service_instances/instance_id", HTTPStatus.OK, None, "v2", "service_instances", "PUT_{id}_response.json" + ) + service_instance = self.client.v2.service_instances.update("instance_id", instance_name="new-name", tags=["other-tag"]) + self.client.put.assert_called_with( + self.client.put.return_value.url, json=dict(name="new-name", tags=["other-tag"]), params=None + ) self.assertIsNotNone(service_instance) - def test_update(self): + def test_update2(self): self.client.put.return_value = self.mock_response( - '/v2/service_instances/instance_id', - HTTPStatus.OK, - None, - 'v2', 'service_instances', 'PUT_{id}_response.json') - service_instance = self.client.v2.service_instances.update('instance_id', instance_name='new-name', - tags=['other-tag'], accepts_incomplete=True) - self.client.put.assert_called_with(self.client.put.return_value.url, - json=dict(name='new-name', - tags=['other-tag']), params={'accepts_incomplete': 'true'}) + "/v2/service_instances/instance_id", HTTPStatus.OK, None, "v2", "service_instances", "PUT_{id}_response.json" + ) + service_instance = self.client.v2.service_instances.update( + "instance_id", instance_name="new-name", tags=["other-tag"], accepts_incomplete=True + ) + self.client.put.assert_called_with( + self.client.put.return_value.url, + json=dict(name="new-name", tags=["other-tag"]), + params={"accepts_incomplete": "true"}, + ) self.assertIsNotNone(service_instance) def test_delete_accepts_incomplete(self): - self.client.delete.return_value = self.mock_response( - '/v2/service_instances/instance_id', - HTTPStatus.NO_CONTENT, - None) - self.client.v2.service_instances.remove('instance_id', accepts_incomplete=True) - self.client.delete.assert_called_with(self.client.delete.return_value.url, - params=dict(accepts_incomplete="true")) - - self.client.delete.return_value = self.mock_response( - '/v2/service_instances/instance_id', - HTTPStatus.NO_CONTENT, - None) - self.client.v2.service_instances.remove('instance_id', accepts_incomplete="true") - self.client.delete.assert_called_with(self.client.delete.return_value.url, - params=dict(accepts_incomplete="true")) + self.client.delete.return_value = self.mock_response("/v2/service_instances/instance_id", HTTPStatus.NO_CONTENT, None) + self.client.v2.service_instances.remove("instance_id", accepts_incomplete=True) + self.client.delete.assert_called_with(self.client.delete.return_value.url, params=dict(accepts_incomplete="true")) + + self.client.delete.return_value = self.mock_response("/v2/service_instances/instance_id", HTTPStatus.NO_CONTENT, None) + self.client.v2.service_instances.remove("instance_id", accepts_incomplete="true") + self.client.delete.assert_called_with(self.client.delete.return_value.url, params=dict(accepts_incomplete="true")) def test_delete_purge(self): - self.client.delete.return_value = self.mock_response( - '/v2/service_instances/instance_id', - HTTPStatus.NO_CONTENT, - None) - self.client.v2.service_instances.remove('instance_id', accepts_incomplete=True, purge=True) - self.client.delete.assert_called_with(self.client.delete.return_value.url, - params=dict(accepts_incomplete='true', purge="true")) - - self.client.delete.return_value = self.mock_response( - '/v2/service_instances/instance_id', - HTTPStatus.NO_CONTENT, - None) - self.client.v2.service_instances.remove('instance_id', purge="true") - self.client.delete.assert_called_with(self.client.delete.return_value.url, - params=dict(purge="true")) - - self.client.delete.return_value = self.mock_response( - '/v2/service_instances/instance_id', - HTTPStatus.NO_CONTENT, - None) - self.client.v2.service_instances.remove('instance_id', purge="true") - self.client.delete.assert_called_with(self.client.delete.return_value.url, - params=dict(purge="true")) + self.client.delete.return_value = self.mock_response("/v2/service_instances/instance_id", HTTPStatus.NO_CONTENT, None) + self.client.v2.service_instances.remove("instance_id", accepts_incomplete=True, purge=True) + self.client.delete.assert_called_with( + self.client.delete.return_value.url, params=dict(accepts_incomplete="true", purge="true") + ) + + self.client.delete.return_value = self.mock_response("/v2/service_instances/instance_id", HTTPStatus.NO_CONTENT, None) + self.client.v2.service_instances.remove("instance_id", purge="true") + self.client.delete.assert_called_with(self.client.delete.return_value.url, params=dict(purge="true")) + + self.client.delete.return_value = self.mock_response("/v2/service_instances/instance_id", HTTPStatus.NO_CONTENT, None) + self.client.v2.service_instances.remove("instance_id", purge="true") + self.client.delete.assert_called_with(self.client.delete.return_value.url, params=dict(purge="true")) def test_delete(self): - self.client.delete.return_value = self.mock_response( - '/v2/service_instances/instance_id', - HTTPStatus.NO_CONTENT, - None) - self.client.v2.service_instances.remove('instance_id') - self.client.delete.assert_called_with(self.client.delete.return_value.url, - params={}) + self.client.delete.return_value = self.mock_response("/v2/service_instances/instance_id", HTTPStatus.NO_CONTENT, None) + self.client.v2.service_instances.remove("instance_id") + self.client.delete.assert_called_with(self.client.delete.return_value.url, params={}) def test_entity(self): self.client.get.side_effect = [ self.mock_response( - '/v2/service_instances/instance_id', - HTTPStatus.OK, - None, - 'v2', 'service_instances', 'GET_{id}_response.json'), + "/v2/service_instances/instance_id", HTTPStatus.OK, None, "v2", "service_instances", "GET_{id}_response.json" + ), self.mock_response( - '/v2/spaces/e3138257-8035-4c03-8aba-ab5d35eec0f9', - HTTPStatus.OK, - None, - 'v2', 'spaces', 'GET_{id}_response.json') - , + "/v2/spaces/e3138257-8035-4c03-8aba-ab5d35eec0f9", HTTPStatus.OK, None, "v2", "spaces", "GET_{id}_response.json" + ), self.mock_response( - '/v2/service_instances/df52420f-d5b9-4b86-a7d3-6d7005d1ce96/service_bindings', + "/v2/service_instances/df52420f-d5b9-4b86-a7d3-6d7005d1ce96/service_bindings", HTTPStatus.OK, None, - 'v2', 'service_bindings', 'GET_response.json'), + "v2", + "service_bindings", + "GET_response.json", + ), self.mock_response( - '/v2/service_plans/65740f84-214a-46cf-b8e3-2233d580f293', + "/v2/service_plans/65740f84-214a-46cf-b8e3-2233d580f293", HTTPStatus.OK, None, - 'v2', 'service_plans', 'GET_{id}_response.json'), + "v2", + "service_plans", + "GET_{id}_response.json", + ), self.mock_response( - '/v2/service_instances/df52420f-d5b9-4b86-a7d3-6d7005d1ce96/routes', + "/v2/service_instances/df52420f-d5b9-4b86-a7d3-6d7005d1ce96/routes", HTTPStatus.OK, None, - 'v2', 'routes', 'GET_response.json' + "v2", + "routes", + "GET_response.json", ), self.mock_response( - '/v2/service_instances/df52420f-d5b9-4b86-a7d3-6d7005d1ce96/service_keys', + "/v2/service_instances/df52420f-d5b9-4b86-a7d3-6d7005d1ce96/service_keys", HTTPStatus.OK, None, - 'v2', 'service_keys', 'GET_response.json' - ) + "v2", + "service_keys", + "GET_response.json", + ), ] - service_instance = self.client.v2.service_instances.get('instance_id') + service_instance = self.client.v2.service_instances.get("instance_id") self.assertIsNotNone(service_instance.space()) cpt = reduce(lambda increment, _: increment + 1, service_instance.service_bindings(), 0) @@ -182,28 +170,27 @@ def test_entity(self): self.assertEqual(cpt, 1) cpt = reduce(lambda increment, _: increment + 1, service_instance.service_keys(), 0) self.assertEqual(cpt, 1) - self.client.get.assert_has_calls([call(side_effect.url) for side_effect in self.client.get.side_effect], - any_order=False) + self.client.get.assert_has_calls([call(side_effect.url) for side_effect in self.client.get.side_effect], any_order=False) - @patch.object(sys, 'argv', ['main', 'list_service_instances']) + @patch.object(sys, "argv", ["main", "list_service_instances"]) def test_main_list_service_instances(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = self.mock_response('/v2/service_instances', - HTTPStatus.OK, - None, - 'v2', 'service_instances', 'GET_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.get.return_value = self.mock_response( + "/v2/service_instances", HTTPStatus.OK, None, "v2", "service_instances", "GET_response.json" + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) - @patch.object(sys, 'argv', ['main', 'get_service_instance', 'df52420f-d5b9-4b86-a7d3-6d7005d1ce96']) + @patch.object(sys, "argv", ["main", "get_service_instance", "df52420f-d5b9-4b86-a7d3-6d7005d1ce96"]) def test_main_get_service_instance(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): self.client.get.return_value = self.mock_response( - '/v2/service_instances/df52420f-d5b9-4b86-a7d3-6d7005d1ce96', + "/v2/service_instances/df52420f-d5b9-4b86-a7d3-6d7005d1ce96", HTTPStatus.OK, None, - 'v2', 'service_instances', 'GET_{id}_response.json') + "v2", + "service_instances", + "GET_{id}_response.json", + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) diff --git a/test/v2/test_service_keys.py b/test/v2/test_service_keys.py index cb28c92..2a6dd8e 100644 --- a/test/v2/test_service_keys.py +++ b/test/v2/test_service_keys.py @@ -19,93 +19,86 @@ def setUp(self): def test_list(self): self.client.get.return_value = self.mock_response( - '/v2/service_keys?q=service_instance_guid%3Ainstance_guid', + "/v2/service_keys?q=service_instance_guid%3Ainstance_guid", HTTPStatus.OK, None, - 'v2', 'service_keys', 'GET_response.json') - cpt = reduce(lambda increment, _: increment + 1, - self.client.v2.service_keys.list(service_instance_guid='instance_guid'), 0) + "v2", + "service_keys", + "GET_response.json", + ) + cpt = reduce( + lambda increment, _: increment + 1, self.client.v2.service_keys.list(service_instance_guid="instance_guid"), 0 + ) self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(cpt, 1) def test_get(self): self.client.get.return_value = self.mock_response( - '/v2/service_keys/key_id', - HTTPStatus.OK, - None, - 'v2', 'service_keys', 'GET_{id}_response.json') - result = self.client.v2.service_keys.get('key_id') + "/v2/service_keys/key_id", HTTPStatus.OK, None, "v2", "service_keys", "GET_{id}_response.json" + ) + result = self.client.v2.service_keys.get("key_id") self.client.get.assert_called_with(self.client.get.return_value.url) self.assertIsNotNone(result) def test_create(self): self.client.post.return_value = self.mock_response( - '/v2/service_keys', - HTTPStatus.CREATED, - None, - 'v2', 'service_keys', 'POST_response.json') - service_key = self.client.v2.service_keys.create('service_instance_id', 'name-127') - self.client.post.assert_called_with(self.client.post.return_value.url, - json=dict(service_instance_guid='service_instance_id', - name='name-127') - ) + "/v2/service_keys", HTTPStatus.CREATED, None, "v2", "service_keys", "POST_response.json" + ) + service_key = self.client.v2.service_keys.create("service_instance_id", "name-127") + self.client.post.assert_called_with( + self.client.post.return_value.url, json=dict(service_instance_guid="service_instance_id", name="name-127") + ) self.assertIsNotNone(service_key) def test_delete(self): - self.client.delete.return_value = self.mock_response( - '/v2/service_keys/key_id', - HTTPStatus.NO_CONTENT, - None) - self.client.v2.service_keys.remove('key_id') + self.client.delete.return_value = self.mock_response("/v2/service_keys/key_id", HTTPStatus.NO_CONTENT, None) + self.client.v2.service_keys.remove("key_id") self.client.delete.assert_called_with(self.client.delete.return_value.url) - @patch.object(sys, 'argv', ['main', 'list_service_keys']) + @patch.object(sys, "argv", ["main", "list_service_keys"]) def test_main_list_service_keys(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = self.mock_response('/v2/service_keys', - HTTPStatus.OK, - None, - 'v2', 'service_keys', 'GET_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.get.return_value = self.mock_response( + "/v2/service_keys", HTTPStatus.OK, None, "v2", "service_keys", "GET_response.json" + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) - @patch.object(sys, 'argv', ['main', 'get_service_key', '67755c27-28ed-4087-9688-c07d92f3bcc9']) + @patch.object(sys, "argv", ["main", "get_service_key", "67755c27-28ed-4087-9688-c07d92f3bcc9"]) def test_main_get_service_key(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = self.mock_response('/v2/service_keys/67755c27-28ed-4087-9688-c07d92f3bcc9', - HTTPStatus.OK, - None, - 'v2', 'service_keys', 'GET_{id}_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.get.return_value = self.mock_response( + "/v2/service_keys/67755c27-28ed-4087-9688-c07d92f3bcc9", + HTTPStatus.OK, + None, + "v2", + "service_keys", + "GET_{id}_response.json", + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) - @patch.object(sys, 'argv', ['main', 'create_service_key', json.dumps( - dict(service_instance_guid='service_instance_id', - name='name-127'))]) + @patch.object( + sys, + "argv", + ["main", "create_service_key", json.dumps(dict(service_instance_guid="service_instance_id", name="name-127"))], + ) def test_main_create_service_key(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): self.client.post.return_value = self.mock_response( - '/v2/service_keys', - HTTPStatus.CREATED, - None, - 'v2', 'service_keys', 'POST_response.json') + "/v2/service_keys", HTTPStatus.CREATED, None, "v2", "service_keys", "POST_response.json" + ) main.main() - self.client.post.assert_called_with(self.client.post.return_value.url, - json=dict(service_instance_guid='service_instance_id', - name='name-127') - ) + self.client.post.assert_called_with( + self.client.post.return_value.url, json=dict(service_instance_guid="service_instance_id", name="name-127") + ) - @patch.object(sys, 'argv', ['main', 'delete_service_key', '67755c27-28ed-4087-9688-c07d92f3bcc9']) + @patch.object(sys, "argv", ["main", "delete_service_key", "67755c27-28ed-4087-9688-c07d92f3bcc9"]) def test_main_delete_service_key(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): self.client.delete.return_value = self.mock_response( - '/v2/service_keys/67755c27-28ed-4087-9688-c07d92f3bcc9', - HTTPStatus.NO_CONTENT, - None) + "/v2/service_keys/67755c27-28ed-4087-9688-c07d92f3bcc9", HTTPStatus.NO_CONTENT, None + ) main.main() self.client.delete.assert_called_with(self.client.delete.return_value.url) main.main() diff --git a/test/v2/test_service_plan_visibilities.py b/test/v2/test_service_plan_visibilities.py index a8532f9..9a70a57 100644 --- a/test/v2/test_service_plan_visibilities.py +++ b/test/v2/test_service_plan_visibilities.py @@ -19,108 +19,104 @@ def setUp(self): def test_list(self): self.client.get.return_value = self.mock_response( - '/v2/service_plan_visibilities?q=space_guid%3Aspace_guid', + "/v2/service_plan_visibilities?q=space_guid%3Aspace_guid", HTTPStatus.OK, None, - 'v2', 'service_plan_visibilities', 'GET_response.json') - cpt = reduce(lambda increment, _: increment + 1, - self.client.v2.service_plan_visibilities.list(space_guid='space_guid'), 0) + "v2", + "service_plan_visibilities", + "GET_response.json", + ) + cpt = reduce( + lambda increment, _: increment + 1, self.client.v2.service_plan_visibilities.list(space_guid="space_guid"), 0 + ) self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(cpt, 1) def test_get(self): self.client.get.return_value = self.mock_response( - '/v2/service_plan_visibilities/guid', - HTTPStatus.OK, - None, - 'v2', 'service_plan_visibilities', 'GET_{id}_response.json') - result = self.client.v2.service_plan_visibilities.get('guid') + "/v2/service_plan_visibilities/guid", HTTPStatus.OK, None, "v2", "service_plan_visibilities", "GET_{id}_response.json" + ) + result = self.client.v2.service_plan_visibilities.get("guid") self.client.get.assert_called_with(self.client.get.return_value.url) self.assertIsNotNone(result) def test_create(self): self.client.post.return_value = self.mock_response( - '/v2/service_plan_visibilities', - HTTPStatus.CREATED, - None, - 'v2', 'service_plan_visibilities', 'POST_response.json') - service_plan_visibilities = self.client.v2.service_plan_visibilities.create('service_plan_guid', - 'organization_guid') - self.client.post.assert_called_with(self.client.post.return_value.url, - json=dict(service_plan_guid='service_plan_guid', - organization_guid='organization_guid')) + "/v2/service_plan_visibilities", HTTPStatus.CREATED, None, "v2", "service_plan_visibilities", "POST_response.json" + ) + service_plan_visibilities = self.client.v2.service_plan_visibilities.create("service_plan_guid", "organization_guid") + self.client.post.assert_called_with( + self.client.post.return_value.url, + json=dict(service_plan_guid="service_plan_guid", organization_guid="organization_guid"), + ) self.assertIsNotNone(service_plan_visibilities) def test_update(self): self.client.put.return_value = self.mock_response( - '/v2/service_plan_visibilities/guid', - HTTPStatus.OK, - None, - 'v2', 'service_plan_visibilities', 'PUT_{id}_response.json') - service_plan_visibilities = \ - self.client.v2.service_plan_visibilities.update('guid', - service_plan_guid='service_plan_guid', - organization_guid='organization_guid') - self.client.put.assert_called_with(self.client.put.return_value.url, - json=dict(service_plan_guid='service_plan_guid', - organization_guid='organization_guid')) + "/v2/service_plan_visibilities/guid", HTTPStatus.OK, None, "v2", "service_plan_visibilities", "PUT_{id}_response.json" + ) + service_plan_visibilities = self.client.v2.service_plan_visibilities.update( + "guid", service_plan_guid="service_plan_guid", organization_guid="organization_guid" + ) + self.client.put.assert_called_with( + self.client.put.return_value.url, + json=dict(service_plan_guid="service_plan_guid", organization_guid="organization_guid"), + ) self.assertIsNotNone(service_plan_visibilities) def test_delete(self): - self.client.delete.return_value = self.mock_response( - '/v2/service_plan_visibilities/guid', - HTTPStatus.NO_CONTENT, - None) - self.client.v2.service_plan_visibilities.remove('guid') + self.client.delete.return_value = self.mock_response("/v2/service_plan_visibilities/guid", HTTPStatus.NO_CONTENT, None) + self.client.v2.service_plan_visibilities.remove("guid") self.client.delete.assert_called_with(self.client.delete.return_value.url) - @patch.object(sys, 'argv', ['main', 'create_service_plan_visibility', json.dumps( - dict(service_plan_guid='service-plan-id', - organization_guid='organization-id'))]) + @patch.object( + sys, + "argv", + [ + "main", + "create_service_plan_visibility", + json.dumps(dict(service_plan_guid="service-plan-id", organization_guid="organization-id")), + ], + ) def test_main_create_service_plan_visibility(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): self.client.post.return_value = self.mock_response( - '/v2/service_plan_visibilities', - HTTPStatus.CREATED, - None, - 'v2', 'service_plan_visibilities', 'POST_response.json') + "/v2/service_plan_visibilities", HTTPStatus.CREATED, None, "v2", "service_plan_visibilities", "POST_response.json" + ) main.main() - self.client.post.assert_called_with(self.client.post.return_value.url, - json=dict(service_plan_guid='service-plan-id', - organization_guid='organization-id') - ) + self.client.post.assert_called_with( + self.client.post.return_value.url, + json=dict(service_plan_guid="service-plan-id", organization_guid="organization-id"), + ) - @patch.object(sys, 'argv', ['main', 'list_service_plan_visibilities']) + @patch.object(sys, "argv", ["main", "list_service_plan_visibilities"]) def test_main_list_service_plan_visibilities(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = self.mock_response('/v2/service_plan_visibilities', - HTTPStatus.OK, - None, - 'v2', 'service_plan_visibilities', 'GET_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.get.return_value = self.mock_response( + "/v2/service_plan_visibilities", HTTPStatus.OK, None, "v2", "service_plan_visibilities", "GET_response.json" + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) - @patch.object(sys, 'argv', ['main', 'get_service_plan_visibility', 'a353104b-1290-418c-bc03-0e647afd0853']) + @patch.object(sys, "argv", ["main", "get_service_plan_visibility", "a353104b-1290-418c-bc03-0e647afd0853"]) def test_main_get_service_plan_visibility(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): self.client.get.return_value = self.mock_response( - '/v2/service_plan_visibilities/a353104b-1290-418c-bc03-0e647afd0853', + "/v2/service_plan_visibilities/a353104b-1290-418c-bc03-0e647afd0853", HTTPStatus.OK, None, - 'v2', 'service_plan_visibilities', 'GET_{id}_response.json') + "v2", + "service_plan_visibilities", + "GET_{id}_response.json", + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) - @patch.object(sys, 'argv', ['main', 'delete_service_plan_visibility', '906775ea-622e-4bc7-af5d-9aab3b652f81']) + @patch.object(sys, "argv", ["main", "delete_service_plan_visibility", "906775ea-622e-4bc7-af5d-9aab3b652f81"]) def test_main_delete_service_plan_visibility(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): self.client.delete.return_value = self.mock_response( - '/v2/service_plan_visibilities/906775ea-622e-4bc7-af5d-9aab3b652f81', - HTTPStatus.NO_CONTENT, - None) + "/v2/service_plan_visibilities/906775ea-622e-4bc7-af5d-9aab3b652f81", HTTPStatus.NO_CONTENT, None + ) main.main() self.client.delete.assert_called_with(self.client.delete.return_value.url) diff --git a/test/v2/test_service_plans.py b/test/v2/test_service_plans.py index a3820bb..cc2c55a 100644 --- a/test/v2/test_service_plans.py +++ b/test/v2/test_service_plans.py @@ -18,81 +18,81 @@ def setUp(self): def test_list(self): self.client.get.return_value = self.mock_response( - '/v2/service_plans?q=service_guid%3Aservice_id', - HTTPStatus.OK, - None, - 'v2', 'service_plans', 'GET_response.json') - cpt = reduce(lambda increment, _: increment + 1, self.client.v2.service_plans.list(service_guid='service_id'), - 0) + "/v2/service_plans?q=service_guid%3Aservice_id", HTTPStatus.OK, None, "v2", "service_plans", "GET_response.json" + ) + cpt = reduce(lambda increment, _: increment + 1, self.client.v2.service_plans.list(service_guid="service_id"), 0) self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(cpt, 1) def test_get(self): self.client.get.return_value = self.mock_response( - '/v2/service_plans/plan_id', - HTTPStatus.OK, - None, - 'v2', 'service_plans', 'GET_{id}_response.json') - result = self.client.v2.service_plans.get('plan_id') + "/v2/service_plans/plan_id", HTTPStatus.OK, None, "v2", "service_plans", "GET_{id}_response.json" + ) + result = self.client.v2.service_plans.get("plan_id") self.client.get.assert_called_with(self.client.get.return_value.url) self.assertIsNotNone(result) def test_list_instances(self): self.client.get.return_value = self.mock_response( - '/v2/service_plans/plan_id/service_instances?q=space_guid%3Aspace_id', + "/v2/service_plans/plan_id/service_instances?q=space_guid%3Aspace_id", HTTPStatus.OK, None, - 'v2', 'apps', 'GET_{id}_routes_response.json') - cpt = reduce(lambda increment, _: increment + 1, - self.client.v2.service_plans.list_instances('plan_id', space_guid='space_id'), 0) + "v2", + "apps", + "GET_{id}_routes_response.json", + ) + cpt = reduce( + lambda increment, _: increment + 1, self.client.v2.service_plans.list_instances("plan_id", space_guid="space_id"), 0 + ) self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(cpt, 1) def test_entity(self): self.client.get.side_effect = [ + self.mock_response("/v2/service_plans/plan_id", HTTPStatus.OK, None, "v2", "service_plans", "GET_{id}_response.json"), self.mock_response( - '/v2/service_plans/plan_id', + "/v2/services/6a4abae6-93e0-438b-aaa2-5ae67f3a069d", HTTPStatus.OK, None, - 'v2', 'service_plans', 'GET_{id}_response.json'), + "v2", + "services", + "GET_{id}_response.json", + ), self.mock_response( - '/v2/services/6a4abae6-93e0-438b-aaa2-5ae67f3a069d', + "/v2/service_plans/5d8f3b0f-6b5b-487f-8fed-4c2d9b812a72/service_instances", HTTPStatus.OK, None, - 'v2', 'services', 'GET_{id}_response.json') - , - self.mock_response( - '/v2/service_plans/5d8f3b0f-6b5b-487f-8fed-4c2d9b812a72/service_instances', - HTTPStatus.OK, - None, - 'v2', 'service_instances', 'GET_response.json') + "v2", + "service_instances", + "GET_response.json", + ), ] - service_plan = self.client.v2.service_plans.get('plan_id') + service_plan = self.client.v2.service_plans.get("plan_id") self.assertIsNotNone(service_plan.service()) cpt = reduce(lambda increment, _: increment + 1, service_plan.service_instances(), 0) self.assertEqual(cpt, 1) - self.client.get.assert_has_calls([call(side_effect.url) for side_effect in self.client.get.side_effect], - any_order=False) + self.client.get.assert_has_calls([call(side_effect.url) for side_effect in self.client.get.side_effect], any_order=False) - @patch.object(sys, 'argv', ['main', 'list_service_plans']) + @patch.object(sys, "argv", ["main", "list_service_plans"]) def test_main_list_service_plans(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = self.mock_response('/v2/service_plans', - HTTPStatus.OK, - None, - 'v2', 'service_plans', 'GET_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.get.return_value = self.mock_response( + "/v2/service_plans", HTTPStatus.OK, None, "v2", "service_plans", "GET_response.json" + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) - @patch.object(sys, 'argv', ['main', 'get_service_plan', '5d8f3b0f-6b5b-487f-8fed-4c2d9b812a72']) + @patch.object(sys, "argv", ["main", "get_service_plan", "5d8f3b0f-6b5b-487f-8fed-4c2d9b812a72"]) def test_main_get_service_plan(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = self.mock_response('/v2/service_plans/5d8f3b0f-6b5b-487f-8fed-4c2d9b812a72', - HTTPStatus.OK, - None, - 'v2', 'service_plans', 'GET_{id}_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.get.return_value = self.mock_response( + "/v2/service_plans/5d8f3b0f-6b5b-487f-8fed-4c2d9b812a72", + HTTPStatus.OK, + None, + "v2", + "service_plans", + "GET_{id}_response.json", + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) diff --git a/test/v2/test_services.py b/test/v2/test_services.py index a49b6d4..6c28106 100644 --- a/test/v2/test_services.py +++ b/test/v2/test_services.py @@ -17,62 +17,57 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = self.mock_response('/v2/services?q=label%3Asome_label', - HTTPStatus.OK, - None, - 'v2', 'services', 'GET_response.json') - cpt = reduce(lambda increment, _: increment + 1, self.client.v2.services.list(label='some_label'), 0) + self.client.get.return_value = self.mock_response( + "/v2/services?q=label%3Asome_label", HTTPStatus.OK, None, "v2", "services", "GET_response.json" + ) + cpt = reduce(lambda increment, _: increment + 1, self.client.v2.services.list(label="some_label"), 0) self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(cpt, 1) def test_get(self): self.client.get.return_value = self.mock_response( - '/v2/services/service_id', - HTTPStatus.OK, - None, - 'v2', 'services', 'GET_{id}_response.json') - result = self.client.v2.services.get('service_id') + "/v2/services/service_id", HTTPStatus.OK, None, "v2", "services", "GET_{id}_response.json" + ) + result = self.client.v2.services.get("service_id") self.client.get.assert_called_with(self.client.get.return_value.url) self.assertIsNotNone(result) def test_entity(self): self.client.get.side_effect = [ + self.mock_response("/v2/services/service_id", HTTPStatus.OK, None, "v2", "services", "GET_{id}_response.json"), self.mock_response( - '/v2/services/service_id', - HTTPStatus.OK, - None, - 'v2', 'services', 'GET_{id}_response.json'), - self.mock_response( - '/v2/services/2c883dbb-a726-4ecf-a0b7-d65588897e7f/service_plans', + "/v2/services/2c883dbb-a726-4ecf-a0b7-d65588897e7f/service_plans", HTTPStatus.OK, None, - 'v2', 'service_plans', 'GET_response.json') - + "v2", + "service_plans", + "GET_response.json", + ), ] - service = self.client.v2.services.get('service_id') + service = self.client.v2.services.get("service_id") cpt = reduce(lambda increment, _: increment + 1, service.service_plans(), 0) self.assertEqual(cpt, 1) - self.client.get.assert_has_calls([call(side_effect.url) for side_effect in self.client.get.side_effect], - any_order=False) + self.client.get.assert_has_calls([call(side_effect.url) for side_effect in self.client.get.side_effect], any_order=False) - @patch.object(sys, 'argv', ['main', 'list_services']) + @patch.object(sys, "argv", ["main", "list_services"]) def test_main_list_services(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = self.mock_response('/v2/services', - HTTPStatus.OK, - None, - 'v2', 'services', 'GET_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.get.return_value = self.mock_response( + "/v2/services", HTTPStatus.OK, None, "v2", "services", "GET_response.json" + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) - @patch.object(sys, 'argv', ['main', 'get_service', '2c883dbb-a726-4ecf-a0b7-d65588897e7f']) + @patch.object(sys, "argv", ["main", "get_service", "2c883dbb-a726-4ecf-a0b7-d65588897e7f"]) def test_main_get_service(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = self.mock_response('/v2/services/2c883dbb-a726-4ecf-a0b7-d65588897e7f', - HTTPStatus.OK, - None, - 'v2', 'services', 'GET_{id}_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.get.return_value = self.mock_response( + "/v2/services/2c883dbb-a726-4ecf-a0b7-d65588897e7f", + HTTPStatus.OK, + None, + "v2", + "services", + "GET_{id}_response.json", + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) diff --git a/test/v2/test_spaces.py b/test/v2/test_spaces.py index 6db230f..8307318 100644 --- a/test/v2/test_spaces.py +++ b/test/v2/test_spaces.py @@ -17,74 +17,66 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = self.mock_response('/v2/spaces?q=organization_guid%3Aorg_id', - HTTPStatus.OK, - None, - 'v2', 'spaces', 'GET_response.json') - cpt = reduce(lambda increment, _: increment + 1, self.client.v2.spaces.list(organization_guid='org_id'), 0) + self.client.get.return_value = self.mock_response( + "/v2/spaces?q=organization_guid%3Aorg_id", HTTPStatus.OK, None, "v2", "spaces", "GET_response.json" + ) + cpt = reduce(lambda increment, _: increment + 1, self.client.v2.spaces.list(organization_guid="org_id"), 0) self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(cpt, 1) def test_get(self): self.client.get.return_value = self.mock_response( - '/v2/spaces/space_id', - HTTPStatus.OK, - None, - 'v2', 'spaces', 'GET_{id}_response.json') - result = self.client.v2.spaces.get('space_id') + "/v2/spaces/space_id", HTTPStatus.OK, None, "v2", "spaces", "GET_{id}_response.json" + ) + result = self.client.v2.spaces.get("space_id") self.client.get.assert_called_with(self.client.get.return_value.url) self.assertIsNotNone(result) def test_entity(self): self.client.get.side_effect = [ + self.mock_response("/v2/spaces/space_id", HTTPStatus.OK, None, "v2", "spaces", "GET_{id}_response.json"), self.mock_response( - '/v2/spaces/space_id', - HTTPStatus.OK, - None, - 'v2', 'spaces', 'GET_{id}_response.json'), - self.mock_response( - '/v2/organizations/d7d77408-a250-45e3-8de5-71fcf199bbab', + "/v2/organizations/d7d77408-a250-45e3-8de5-71fcf199bbab", HTTPStatus.OK, None, - 'v2', 'organizations', 'GET_{id}_response.json'), + "v2", + "organizations", + "GET_{id}_response.json", + ), self.mock_response( - '/v2/spaces/2d745a4b-67e3-4398-986e-2adbcf8f7ec9/apps', - HTTPStatus.OK, - None, - 'v2', 'apps', 'GET_response.json'), + "/v2/spaces/2d745a4b-67e3-4398-986e-2adbcf8f7ec9/apps", HTTPStatus.OK, None, "v2", "apps", "GET_response.json" + ), self.mock_response( - '/v2/spaces/2d745a4b-67e3-4398-986e-2adbcf8f7ec9/service_instances', + "/v2/spaces/2d745a4b-67e3-4398-986e-2adbcf8f7ec9/service_instances", HTTPStatus.OK, None, - 'v2', 'service_instances', 'GET_response.json') + "v2", + "service_instances", + "GET_response.json", + ), ] - space = self.client.v2.spaces.get('space_id') + space = self.client.v2.spaces.get("space_id") self.assertIsNotNone(space.organization()) cpt = reduce(lambda increment, _: increment + 1, space.apps(), 0) self.assertEqual(cpt, 3) cpt = reduce(lambda increment, _: increment + 1, space.service_instances(), 0) self.assertEqual(cpt, 1) - self.client.get.assert_has_calls([call(side_effect.url) for side_effect in self.client.get.side_effect], - any_order=False) + self.client.get.assert_has_calls([call(side_effect.url) for side_effect in self.client.get.side_effect], any_order=False) - @patch.object(sys, 'argv', ['main', 'list_spaces']) + @patch.object(sys, "argv", ["main", "list_spaces"]) def test_main_list_spaces(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = self.mock_response('/v2/spaces', - HTTPStatus.OK, - None, - 'v2', 'spaces', 'GET_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.get.return_value = self.mock_response( + "/v2/spaces", HTTPStatus.OK, None, "v2", "spaces", "GET_response.json" + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) - @patch.object(sys, 'argv', ['main', 'get_space', '2d745a4b-67e3-4398-986e-2adbcf8f7ec9']) + @patch.object(sys, "argv", ["main", "get_space", "2d745a4b-67e3-4398-986e-2adbcf8f7ec9"]) def test_main_get_spaces(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = self.mock_response('/v2/spaces/2d745a4b-67e3-4398-986e-2adbcf8f7ec9', - HTTPStatus.OK, - None, - 'v2', 'spaces', 'GET_{id}_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.get.return_value = self.mock_response( + "/v2/spaces/2d745a4b-67e3-4398-986e-2adbcf8f7ec9", HTTPStatus.OK, None, "v2", "spaces", "GET_{id}_response.json" + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) diff --git a/test/v3/test_apps.py b/test/v3/test_apps.py index b18b736..c34f47a 100644 --- a/test/v3/test_apps.py +++ b/test/v3/test_apps.py @@ -15,74 +15,65 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = self.mock_response('/v3/apps', - HTTPStatus.OK, - None, - 'v3', 'apps', 'GET_response.json') - all_applications = [ - application for application in self.client.v3.apps.list()] + self.client.get.return_value = self.mock_response("/v3/apps", HTTPStatus.OK, None, "v3", "apps", "GET_response.json") + all_applications = [application for application in self.client.v3.apps.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(2, len(all_applications)) - self.assertEqual(all_applications[0]['name'], "my_app") + self.assertEqual(all_applications[0]["name"], "my_app") self.assertIsInstance(all_applications[0], Entity) def test_get(self): - self.client.get.return_value = self.mock_response('/v3/apps/app_id', - HTTPStatus.OK, - None, - 'v3', 'apps', 'GET_{id}_response.json') - application = self.client.v3.apps.get('app_id') + self.client.get.return_value = self.mock_response( + "/v3/apps/app_id", HTTPStatus.OK, None, "v3", "apps", "GET_{id}_response.json" + ) + application = self.client.v3.apps.get("app_id") self.client.get.assert_called_with(self.client.get.return_value.url) - self.assertEqual("my_app", application['name']) + self.assertEqual("my_app", application["name"]) self.assertIsInstance(application, Entity) def test_get_then_space(self): - get_app = self.mock_response( - '/v3/apps/app_id', HTTPStatus.OK, None, 'v3', 'apps', 'GET_{id}_response.json') - get_space = self.mock_response('/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576', HTTPStatus.OK, None, - 'v3', 'spaces', 'GET_{id}_response.json') - self.client.get.side_effect = [ - get_app, - get_space - ] - space = self.client.v3.apps.get('app_id').space() + get_app = self.mock_response("/v3/apps/app_id", HTTPStatus.OK, None, "v3", "apps", "GET_{id}_response.json") + get_space = self.mock_response( + "/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576", HTTPStatus.OK, None, "v3", "spaces", "GET_{id}_response.json" + ) + self.client.get.side_effect = [get_app, get_space] + space = self.client.v3.apps.get("app_id").space() # self.client.get.assert_has_calls([call(get_app.url), # call(get_space.url)], # any_order=False) - self.assertEqual("my-space", space['name']) + self.assertEqual("my-space", space["name"]) def test_get_then_start(self): - self.client.get.return_value = self.mock_response('/v3/apps/app_id', HTTPStatus.OK, None, - 'v3', 'apps', 'GET_{id}_response.json') - self.client.post.return_value = self.mock_response('/v3/apps/app_id/actions/start', HTTPStatus.OK, None, - 'v3', 'apps', 'POST_{id}_actions_start_response.json') + self.client.get.return_value = self.mock_response( + "/v3/apps/app_id", HTTPStatus.OK, None, "v3", "apps", "GET_{id}_response.json" + ) + self.client.post.return_value = self.mock_response( + "/v3/apps/app_id/actions/start", HTTPStatus.OK, None, "v3", "apps", "POST_{id}_actions_start_response.json" + ) - app = self.client.v3.apps.get('app_id').start() + app = self.client.v3.apps.get("app_id").start() self.client.get.assert_called_with(self.client.get.return_value.url) - self.client.post.assert_called_with( - self.client.post.return_value.url, files=None, json=None) - self.assertEqual("my_app", app['name']) + self.client.post.assert_called_with(self.client.post.return_value.url, files=None, json=None) + self.assertEqual("my_app", app["name"]) self.assertIsInstance(app, Entity) def test_remove(self): - self.client.delete.return_value = self.mock_response( - '/v3/apps/app_id', HTTPStatus.NO_CONTENT, None) - self.client.v3.apps.remove('app_id') - self.client.delete.assert_called_with( - self.client.delete.return_value.url) + self.client.delete.return_value = self.mock_response("/v3/apps/app_id", HTTPStatus.NO_CONTENT, None) + self.client.v3.apps.remove("app_id") + self.client.delete.assert_called_with(self.client.delete.return_value.url) def test_get_env(self): - self.client.get.return_value = self.mock_response('/v3/apps/app_id/env', HTTPStatus.OK, None, - 'v3', 'apps', 'GET_{id}_env_response.json') - env = self.client.v3.apps.get_env('app_id') + self.client.get.return_value = self.mock_response( + "/v3/apps/app_id/env", HTTPStatus.OK, None, "v3", "apps", "GET_{id}_env_response.json" + ) + env = self.client.v3.apps.get_env("app_id") self.assertIsInstance(env, JsonObject) - self.assertEquals(env['application_env_json'] - ['VCAP_APPLICATION']['limits']['fds'], 16384) + self.assertEquals(env["application_env_json"]["VCAP_APPLICATION"]["limits"]["fds"], 16384) def test_get_routes(self): - self.client.get.return_value = self.mock_response('/v3/apps/app_id/routes', HTTPStatus.OK, None, - 'v3', 'apps', 'GET_{id}_routes_response.json') - routes = self.client.v3.apps.get_routes('app_id') + self.client.get.return_value = self.mock_response( + "/v3/apps/app_id/routes", HTTPStatus.OK, None, "v3", "apps", "GET_{id}_routes_response.json" + ) + routes = self.client.v3.apps.get_routes("app_id") self.assertIsInstance(routes, JsonObject) - self.assertEquals(routes['resources'][0]['destinations'][0]['guid'], - "385bf117-17f5-4689-8c5c-08c6cc821fed") + self.assertEquals(routes["resources"][0]["destinations"][0]["guid"], "385bf117-17f5-4689-8c5c-08c6cc821fed") diff --git a/test/v3/test_buildpacks.py b/test/v3/test_buildpacks.py index 585c0cd..a90561c 100644 --- a/test/v3/test_buildpacks.py +++ b/test/v3/test_buildpacks.py @@ -17,99 +17,84 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = self.mock_response('/v3/buildpacks', - HTTPStatus.OK, - None, - 'v3', 'buildpacks', 'GET_response.json') + self.client.get.return_value = self.mock_response( + "/v3/buildpacks", HTTPStatus.OK, None, "v3", "buildpacks", "GET_response.json" + ) all_buildpacks = [buildpack for buildpack in self.client.v3.buildpacks.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(1, len(all_buildpacks)) - self.assertEqual(all_buildpacks[0]['name'], "my-buildpack") + self.assertEqual(all_buildpacks[0]["name"], "my-buildpack") self.assertIsInstance(all_buildpacks[0], Entity) def test_get(self): self.client.get.return_value = self.mock_response( - '/v3/buildpacks/buildpack_id', - HTTPStatus.OK, - None, - 'v3', 'buildpacks', 'GET_{id}_response.json') - result = self.client.v3.buildpacks.get('buildpack_id') + "/v3/buildpacks/buildpack_id", HTTPStatus.OK, None, "v3", "buildpacks", "GET_{id}_response.json" + ) + result = self.client.v3.buildpacks.get("buildpack_id") self.client.get.assert_called_with(self.client.get.return_value.url) self.assertIsNotNone(result) def test_update(self): self.client.patch.return_value = self.mock_response( - '/v3/buildpacks/buildpack_id', - HTTPStatus.OK, - None, - 'v3', 'buildpacks', 'PATCH_{id}_response.json') - result = self.client.v3.buildpacks.update('buildpack_id', 'ruby_buildpack', - enabled=True, - position=42, - stack='windows64') - self.client.patch.assert_called_with(self.client.patch.return_value.url, - json={'locked': False, - 'name': 'ruby_buildpack', - 'enabled': True, - 'position': 42, - 'stack': 'windows64', - 'metadata': { - 'labels': None, - 'annotations': None - } - }) + "/v3/buildpacks/buildpack_id", HTTPStatus.OK, None, "v3", "buildpacks", "PATCH_{id}_response.json" + ) + result = self.client.v3.buildpacks.update("buildpack_id", "ruby_buildpack", enabled=True, position=42, stack="windows64") + self.client.patch.assert_called_with( + self.client.patch.return_value.url, + json={ + "locked": False, + "name": "ruby_buildpack", + "enabled": True, + "position": 42, + "stack": "windows64", + "metadata": {"labels": None, "annotations": None}, + }, + ) self.assertIsNotNone(result) def test_create(self): self.client.post.return_value = self.mock_response( - '/v3/buildpacks', - HTTPStatus.OK, - None, - 'v3', 'buildpacks', 'POST_response.json') - result = self.client.v3.buildpacks.create('ruby_buildpack', - enabled=True, - position=42, - stack='windows64') - self.client.post.assert_called_with(self.client.post.return_value.url, - files=None, - json={'locked': False, - 'name': 'ruby_buildpack', - 'enabled': True, - 'position': 42, - 'stack': 'windows64', - 'metadata': { - 'labels': None, - 'annotations': None - } - }) + "/v3/buildpacks", HTTPStatus.OK, None, "v3", "buildpacks", "POST_response.json" + ) + result = self.client.v3.buildpacks.create("ruby_buildpack", enabled=True, position=42, stack="windows64") + self.client.post.assert_called_with( + self.client.post.return_value.url, + files=None, + json={ + "locked": False, + "name": "ruby_buildpack", + "enabled": True, + "position": 42, + "stack": "windows64", + "metadata": {"labels": None, "annotations": None}, + }, + ) self.assertIsNotNone(result) def test_remove(self): - self.client.delete.return_value = self.mock_response( - '/v3/buildpacks/buildpack_id', - HTTPStatus.NO_CONTENT, - None) - self.client.v3.buildpacks.remove('buildpack_id') + self.client.delete.return_value = self.mock_response("/v3/buildpacks/buildpack_id", HTTPStatus.NO_CONTENT, None) + self.client.v3.buildpacks.remove("buildpack_id") self.client.delete.assert_called_with(self.client.delete.return_value.url) - @patch.object(sys, 'argv', ['main', 'list_buildpacks']) + @patch.object(sys, "argv", ["main", "list_buildpacks"]) def test_main_list_buildpacks(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = self.mock_response('/v3/buildpacks', - HTTPStatus.OK, - None, - 'v3', 'buildpacks', 'GET_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.get.return_value = self.mock_response( + "/v3/buildpacks", HTTPStatus.OK, None, "v3", "buildpacks", "GET_response.json" + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) - @patch.object(sys, 'argv', ['main', 'get_buildpack', '6e72c33b-dff0-4020-8603-bcd8a4eb05e4']) + @patch.object(sys, "argv", ["main", "get_buildpack", "6e72c33b-dff0-4020-8603-bcd8a4eb05e4"]) def test_main_get_buildpack(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = self.mock_response('/v3/buildpacks/6e72c33b-dff0-4020-8603-bcd8a4eb05e4', - HTTPStatus.OK, - None, - 'v3', 'buildpacks', 'GET_{id}_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.get.return_value = self.mock_response( + "/v3/buildpacks/6e72c33b-dff0-4020-8603-bcd8a4eb05e4", + HTTPStatus.OK, + None, + "v3", + "buildpacks", + "GET_{id}_response.json", + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) diff --git a/test/v3/test_domains.py b/test/v3/test_domains.py index de3502d..fb971a4 100644 --- a/test/v3/test_domains.py +++ b/test/v3/test_domains.py @@ -18,142 +18,122 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = self.mock_response('/v3/domains', - HTTPStatus.OK, - None, - 'v3', 'domains', 'GET_response.json') + self.client.get.return_value = self.mock_response( + "/v3/domains", HTTPStatus.OK, None, "v3", "domains", "GET_response.json" + ) all_domains = [domain for domain in self.client.v3.domains.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(1, len(all_domains)) - self.assertEqual(all_domains[0]['name'], "test-domain.com") + self.assertEqual(all_domains[0]["name"], "test-domain.com") self.assertIsInstance(all_domains[0], Entity) for domain in all_domains: self.assertIsInstance(domain, Domain) def test_get(self): self.client.get.return_value = self.mock_response( - '/v3/domains/domain_id', - HTTPStatus.OK, - None, - 'v3', 'domains', 'GET_{id}_response.json') - result = self.client.v3.domains.get('domain_id') + "/v3/domains/domain_id", HTTPStatus.OK, None, "v3", "domains", "GET_{id}_response.json" + ) + result = self.client.v3.domains.get("domain_id") self.client.get.assert_called_with(self.client.get.return_value.url) self.assertIsNotNone(result) self.assertIsInstance(result, Domain) def test_update(self): self.client.patch.return_value = self.mock_response( - '/v3/domains/domain_id', - HTTPStatus.OK, - None, - 'v3', 'domains', 'PATCH_{id}_response.json') - result = self.client.v3.domains.update('domain_id') - self.client.patch.assert_called_with(self.client.patch.return_value.url, - json={'metadata': { - 'labels': None, - 'annotations': None - } - }) + "/v3/domains/domain_id", HTTPStatus.OK, None, "v3", "domains", "PATCH_{id}_response.json" + ) + result = self.client.v3.domains.update("domain_id") + self.client.patch.assert_called_with( + self.client.patch.return_value.url, json={"metadata": {"labels": None, "annotations": None}} + ) self.assertIsNotNone(result) self.assertIsInstance(result, Domain) def test_create(self): self.client.post.return_value = self.mock_response( - '/v3/domains', - HTTPStatus.OK, - None, - 'v3', 'domains', 'POST_response.json') - result = self.client.v3.domains.create('domain_id', - internal=False, - organization=ToOneRelationship('organization-guid'), - shared_organizations=None, - meta_labels=None, - meta_annotations=None) - self.client.post.assert_called_with(self.client.post.return_value.url, - files=None, - json={'name': 'domain_id', - 'internal': False, - 'organization': { - 'data': { - 'guid': 'organization-guid' - } - }, - 'shared_organizations': None, - 'metadata': { - 'labels': None, - 'annotations': None - } - }) + "/v3/domains", HTTPStatus.OK, None, "v3", "domains", "POST_response.json" + ) + result = self.client.v3.domains.create( + "domain_id", + internal=False, + organization=ToOneRelationship("organization-guid"), + shared_organizations=None, + meta_labels=None, + meta_annotations=None, + ) + self.client.post.assert_called_with( + self.client.post.return_value.url, + files=None, + json={ + "name": "domain_id", + "internal": False, + "organization": {"data": {"guid": "organization-guid"}}, + "shared_organizations": None, + "metadata": {"labels": None, "annotations": None}, + }, + ) self.assertIsNotNone(result) self.assertIsInstance(result, Domain) def test_remove(self): - self.client.delete.return_value = self.mock_response( - '/v3/domains/domain_id', - HTTPStatus.NO_CONTENT, - None) - self.client.v3.domains.remove('domain_id') + self.client.delete.return_value = self.mock_response("/v3/domains/domain_id", HTTPStatus.NO_CONTENT, None) + self.client.v3.domains.remove("domain_id") self.client.delete.assert_called_with(self.client.delete.return_value.url) def test_list_domains_for_org(self): - self.client.get.return_value = self.mock_response('/v3/organizations/org_id/domains', - HTTPStatus.OK, - None, - 'v3', 'domains', 'GET_response.json') - all_domains = [domain for domain in self.client.v3.domains.list_domains_for_org('org_id')] + self.client.get.return_value = self.mock_response( + "/v3/organizations/org_id/domains", HTTPStatus.OK, None, "v3", "domains", "GET_response.json" + ) + all_domains = [domain for domain in self.client.v3.domains.list_domains_for_org("org_id")] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(1, len(all_domains)) - self.assertEqual(all_domains[0]['name'], "test-domain.com") + self.assertEqual(all_domains[0]["name"], "test-domain.com") for domain in all_domains: self.assertIsInstance(domain, Domain) def test_share_domain(self): self.client.post.return_value = self.mock_response( - '/v3/domains/domain_id/relationships/shared_organizations', + "/v3/domains/domain_id/relationships/shared_organizations", HTTPStatus.CREATED, None, - 'v3', 'domains', 'POST_{id}_relationships_shared_organizations_response.json') - result = self.client.v3.domains.share_domain('domain_id', - ToManyRelationship('organization-guid-1', 'organization-guid-2')) - self.client.post.assert_called_with(self.client.post.return_value.url, - files=None, - json={ - 'data': [ - {'guid': 'organization-guid-1'}, - {'guid': 'organization-guid-2'} - ] - }) + "v3", + "domains", + "POST_{id}_relationships_shared_organizations_response.json", + ) + result = self.client.v3.domains.share_domain( + "domain_id", ToManyRelationship("organization-guid-1", "organization-guid-2") + ) + self.client.post.assert_called_with( + self.client.post.return_value.url, + files=None, + json={"data": [{"guid": "organization-guid-1"}, {"guid": "organization-guid-2"}]}, + ) self.assertIsNotNone(result) self.assertIsInstance(result, ToManyRelationship) - result.guids[0] = 'organization-guid-1' - result.guids[1] = 'organization-guid-1' + result.guids[0] = "organization-guid-1" + result.guids[1] = "organization-guid-1" def test_unshare_domain(self): self.client.delete.return_value = self.mock_response( - '/v3/domains/domain_id/relationships/shared_organizations/org_id', - HTTPStatus.NO_CONTENT, - None) - self.client.v3.domains.unshare_domain('domain_id', 'org_id') + "/v3/domains/domain_id/relationships/shared_organizations/org_id", HTTPStatus.NO_CONTENT, None + ) + self.client.v3.domains.unshare_domain("domain_id", "org_id") self.client.delete.assert_called_with(self.client.delete.return_value.url) - @patch.object(sys, 'argv', ['main', 'list_domains']) + @patch.object(sys, "argv", ["main", "list_domains"]) def test_main_list_domains(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = self.mock_response('/v3/domains', - HTTPStatus.OK, - None, - 'v3', 'domains', 'GET_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.get.return_value = self.mock_response( + "/v3/domains", HTTPStatus.OK, None, "v3", "domains", "GET_response.json" + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) - @patch.object(sys, 'argv', ['main', 'get_domain', '3a5d3d89-3f89-4f05-8188-8a2b298c79d5']) + @patch.object(sys, "argv", ["main", "get_domain", "3a5d3d89-3f89-4f05-8188-8a2b298c79d5"]) def test_main_get_domain(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = self.mock_response('/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5', - HTTPStatus.OK, - None, - 'v3', 'domains', 'GET_{id}_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.get.return_value = self.mock_response( + "/v3/domains/3a5d3d89-3f89-4f05-8188-8a2b298c79d5", HTTPStatus.OK, None, "v3", "domains", "GET_{id}_response.json" + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) diff --git a/test/v3/test_feature_flags.py b/test/v3/test_feature_flags.py index b2489d6..25fc2b6 100644 --- a/test/v3/test_feature_flags.py +++ b/test/v3/test_feature_flags.py @@ -13,35 +13,29 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = self.mock_response('/v3/feature_flags', - HTTPStatus.OK, - None, - 'v3', 'feature_flags', 'GET_response.json') + self.client.get.return_value = self.mock_response( + "/v3/feature_flags", HTTPStatus.OK, None, "v3", "feature_flags", "GET_response.json" + ) all_feature_flags = [feature_flag for feature_flag in self.client.v3.feature_flags.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(2, len(all_feature_flags)) - self.assertEqual(all_feature_flags[0]['name'], "my_feature_flag") - self.assertEqual(all_feature_flags[1]['name'], "my_second_feature_flag") + self.assertEqual(all_feature_flags[0]["name"], "my_feature_flag") + self.assertEqual(all_feature_flags[1]["name"], "my_second_feature_flag") def test_get(self): - self.client.get.return_value = self.mock_response('/v3/feature_flags/feature_flag_name', - HTTPStatus.OK, - None, - 'v3', 'feature_flags', 'GET_{id}_response.json') - feature_flag = self.client.v3.feature_flags.get('feature_flag_name') + self.client.get.return_value = self.mock_response( + "/v3/feature_flags/feature_flag_name", HTTPStatus.OK, None, "v3", "feature_flags", "GET_{id}_response.json" + ) + feature_flag = self.client.v3.feature_flags.get("feature_flag_name") self.client.get.assert_called_with(self.client.get.return_value.url) - self.assertEqual("my_feature_flag", feature_flag['name']) + self.assertEqual("my_feature_flag", feature_flag["name"]) def test_update(self): self.client.patch.return_value = self.mock_response( - '/v3/feature_flags/feature_flag_name', - HTTPStatus.OK, - None, - 'v3', 'feature_flags', 'PATCH_{id}_response.json') - result = self.client.v3.feature_flags.update('feature_flag_name') - self.client.patch.assert_called_with(self.client.patch.return_value.url, - json={'enabled': True, - 'custom_error_message': None - } - ) + "/v3/feature_flags/feature_flag_name", HTTPStatus.OK, None, "v3", "feature_flags", "PATCH_{id}_response.json" + ) + result = self.client.v3.feature_flags.update("feature_flag_name") + self.client.patch.assert_called_with( + self.client.patch.return_value.url, json={"enabled": True, "custom_error_message": None} + ) self.assertIsNotNone(result) diff --git a/test/v3/test_isolation_segments.py b/test/v3/test_isolation_segments.py index aea2b04..2c26bf1 100644 --- a/test/v3/test_isolation_segments.py +++ b/test/v3/test_isolation_segments.py @@ -14,125 +14,125 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = self.mock_response('/v3/isolation_segments', - HTTPStatus.OK, - None, - 'v3', 'isolation_segments', 'GET_response.json') + self.client.get.return_value = self.mock_response( + "/v3/isolation_segments", HTTPStatus.OK, None, "v3", "isolation_segments", "GET_response.json" + ) all_isolation_segments = [isolation_segment for isolation_segment in self.client.v3.isolation_segments.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(5, len(all_isolation_segments)) - self.assertEqual(all_isolation_segments[0]['name'], "an_isolation_segment") + self.assertEqual(all_isolation_segments[0]["name"], "an_isolation_segment") self.assertIsInstance(all_isolation_segments[0], Entity) for isolation_segment in all_isolation_segments: self.assertIsInstance(isolation_segment, Entity) def test_get(self): self.client.get.return_value = self.mock_response( - '/v3/isolation_segments/isolation_segment_id', + "/v3/isolation_segments/isolation_segment_id", HTTPStatus.OK, None, - 'v3', 'isolation_segments', 'GET_{id}_response.json') - result = self.client.v3.isolation_segments.get('isolation_segment_id') + "v3", + "isolation_segments", + "GET_{id}_response.json", + ) + result = self.client.v3.isolation_segments.get("isolation_segment_id") self.client.get.assert_called_with(self.client.get.return_value.url) self.assertIsNotNone(result) self.assertIsInstance(result, Entity) def test_update(self): self.client.patch.return_value = self.mock_response( - '/v3/isolation_segments/isolation_segment_id', + "/v3/isolation_segments/isolation_segment_id", HTTPStatus.OK, None, - 'v3', 'isolation_segments', 'PATCH_{id}_response.json') - result = self.client.v3.isolation_segments.update('isolation_segment_id', - 'new-name', - meta_labels=dict(key="value")) - self.client.patch.assert_called_with(self.client.patch.return_value.url, - json={ - 'name': 'new-name', - 'metadata': { - 'labels': {'key': 'value'}, - 'annotations': None - } - }) + "v3", + "isolation_segments", + "PATCH_{id}_response.json", + ) + result = self.client.v3.isolation_segments.update("isolation_segment_id", "new-name", meta_labels=dict(key="value")) + self.client.patch.assert_called_with( + self.client.patch.return_value.url, + json={"name": "new-name", "metadata": {"labels": {"key": "value"}, "annotations": None}}, + ) self.assertIsNotNone(result) self.assertIsInstance(result, Entity) def test_create(self): self.client.post.return_value = self.mock_response( - '/v3/isolation_segments', - HTTPStatus.OK, - None, - 'v3', 'isolation_segments', 'POST_response.json') - result = self.client.v3.isolation_segments.create('isolation_segment_id', - meta_labels=dict(key_label="value_label"), - meta_annotations=dict(key_annotation="value_annotation")) - self.client.post.assert_called_with(self.client.post.return_value.url, - files=None, - json={'name': 'isolation_segment_id', - 'metadata': - { - 'labels': {'key_label': 'value_label'}, - 'annotations': {'key_annotation': 'value_annotation'} - } - }) + "/v3/isolation_segments", HTTPStatus.OK, None, "v3", "isolation_segments", "POST_response.json" + ) + result = self.client.v3.isolation_segments.create( + "isolation_segment_id", + meta_labels=dict(key_label="value_label"), + meta_annotations=dict(key_annotation="value_annotation"), + ) + self.client.post.assert_called_with( + self.client.post.return_value.url, + files=None, + json={ + "name": "isolation_segment_id", + "metadata": {"labels": {"key_label": "value_label"}, "annotations": {"key_annotation": "value_annotation"}}, + }, + ) self.assertIsNotNone(result) self.assertIsInstance(result, Entity) def test_remove(self): self.client.delete.return_value = self.mock_response( - '/v3/isolation_segments/isolation_segment_id', - HTTPStatus.NO_CONTENT, - None) - self.client.v3.isolation_segments.remove('isolation_segment_id') + "/v3/isolation_segments/isolation_segment_id", HTTPStatus.NO_CONTENT, None + ) + self.client.v3.isolation_segments.remove("isolation_segment_id") self.client.delete.assert_called_with(self.client.delete.return_value.url) def test_entitle_organizations(self): self.client.post.return_value = self.mock_response( - '/v3/isolation_segments/isolation_segment_id/relationships/organizations', + "/v3/isolation_segments/isolation_segment_id/relationships/organizations", HTTPStatus.OK, None, - 'v3', 'isolation_segments', 'POST_{id}_relationships_organizations_response.json') - result = self.client.v3.isolation_segments.entitle_organizations('isolation_segment_id', 'org_id_1', 'org_id_2') - self.client.post.assert_called_with(self.client.post.return_value.url, - files=None, - json={ - 'data': [ - {'guid': 'org_id_1'}, - {'guid': 'org_id_2'} - ] - }) + "v3", + "isolation_segments", + "POST_{id}_relationships_organizations_response.json", + ) + result = self.client.v3.isolation_segments.entitle_organizations("isolation_segment_id", "org_id_1", "org_id_2") + self.client.post.assert_called_with( + self.client.post.return_value.url, files=None, json={"data": [{"guid": "org_id_1"}, {"guid": "org_id_2"}]} + ) self.assertIsInstance(result, ToManyRelationship) self.assertEqual(2, len(result.guids)) - self.assertIsNotNone(result['links']) + self.assertIsNotNone(result["links"]) def test_revoke_organization(self): self.client.delete.return_value = self.mock_response( - '/v3/isolation_segments/isolation_segment_id/relationships/organizations/org_id', - HTTPStatus.NO_CONTENT, - None) - self.client.v3.isolation_segments.revoke_organization('isolation_segment_id', 'org_id') + "/v3/isolation_segments/isolation_segment_id/relationships/organizations/org_id", HTTPStatus.NO_CONTENT, None + ) + self.client.v3.isolation_segments.revoke_organization("isolation_segment_id", "org_id") self.client.delete.assert_called_with(self.client.delete.return_value.url) def test_list_entitled_organizations(self): self.client.get.return_value = self.mock_response( - '/v3/isolation_segments/isolation_segment_id/relationships/organizations', + "/v3/isolation_segments/isolation_segment_id/relationships/organizations", HTTPStatus.OK, None, - 'v3', 'isolation_segments', 'GET_{id}_relationships_organizations_response.json') - result = self.client.v3.isolation_segments.list_entitled_organizations('isolation_segment_id') + "v3", + "isolation_segments", + "GET_{id}_relationships_organizations_response.json", + ) + result = self.client.v3.isolation_segments.list_entitled_organizations("isolation_segment_id") self.client.get.assert_called_with(self.client.get.return_value.url) self.assertIsInstance(result, ToManyRelationship) self.assertEqual(2, len(result.guids)) - self.assertIsNotNone(result['links']) + self.assertIsNotNone(result["links"]) def test_list_entitled_spaces(self): self.client.get.return_value = self.mock_response( - '/v3/isolation_segments/isolation_segment_id/relationships/spaces', + "/v3/isolation_segments/isolation_segment_id/relationships/spaces", HTTPStatus.OK, None, - 'v3', 'isolation_segments', 'GET_{id}_relationships_spaces_response.json') - result = self.client.v3.isolation_segments.list_entitled_spaces('isolation_segment_id') + "v3", + "isolation_segments", + "GET_{id}_relationships_spaces_response.json", + ) + result = self.client.v3.isolation_segments.list_entitled_spaces("isolation_segment_id") self.client.get.assert_called_with(self.client.get.return_value.url) self.assertIsInstance(result, ToManyRelationship) self.assertEqual(2, len(result.guids)) - self.assertIsNotNone(result['links']) + self.assertIsNotNone(result["links"]) diff --git a/test/v3/test_organizations.py b/test/v3/test_organizations.py index 9d04368..3156850 100644 --- a/test/v3/test_organizations.py +++ b/test/v3/test_organizations.py @@ -17,139 +17,128 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = self.mock_response('/v3/organizations', - HTTPStatus.OK, - None, - 'v3', 'organizations', 'GET_response.json') + self.client.get.return_value = self.mock_response( + "/v3/organizations", HTTPStatus.OK, None, "v3", "organizations", "GET_response.json" + ) all_organizations = [organization for organization in self.client.v3.organizations.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(2, len(all_organizations)) - self.assertEqual(all_organizations[0]['name'], "org1") + self.assertEqual(all_organizations[0]["name"], "org1") self.assertIsInstance(all_organizations[0], Entity) def test_get(self): - self.client.get.return_value = self.mock_response('/v3/organizations/organization_id', - HTTPStatus.OK, - None, - 'v3', 'organizations', 'GET_{id}_response.json') - organization = self.client.v3.organizations.get('organization_id') + self.client.get.return_value = self.mock_response( + "/v3/organizations/organization_id", HTTPStatus.OK, None, "v3", "organizations", "GET_{id}_response.json" + ) + organization = self.client.v3.organizations.get("organization_id") self.client.get.assert_called_with(self.client.get.return_value.url) - self.assertEqual("my-organization", organization['name']) + self.assertEqual("my-organization", organization["name"]) self.assertIsInstance(organization, Entity) def test_update(self): self.client.patch.return_value = self.mock_response( - '/v3/organizations/organization_id', - HTTPStatus.OK, - None, - 'v3', 'organizations', 'PATCH_{id}_response.json') - result = self.client.v3.organizations.update('organization_id', 'my-organization', - suspended=True) - self.client.patch.assert_called_with(self.client.patch.return_value.url, - json={'suspended': True, - 'name': 'my-organization', - 'metadata': { - 'labels': None, - 'annotations': None - } - }) + "/v3/organizations/organization_id", HTTPStatus.OK, None, "v3", "organizations", "PATCH_{id}_response.json" + ) + result = self.client.v3.organizations.update("organization_id", "my-organization", suspended=True) + self.client.patch.assert_called_with( + self.client.patch.return_value.url, + json={"suspended": True, "name": "my-organization", "metadata": {"labels": None, "annotations": None}}, + ) self.assertIsNotNone(result) def test_create(self): self.client.post.return_value = self.mock_response( - '/v3/organizations', - HTTPStatus.OK, - None, - 'v3', 'organizations', 'POST_response.json') - result = self.client.v3.organizations.create('my-organization', - suspended=False) - self.client.post.assert_called_with(self.client.post.return_value.url, - files=None, - json={'name': 'my-organization', - 'suspended': False, - 'metadata': { - 'labels': None, - 'annotations': None - } - }) + "/v3/organizations", HTTPStatus.OK, None, "v3", "organizations", "POST_response.json" + ) + result = self.client.v3.organizations.create("my-organization", suspended=False) + self.client.post.assert_called_with( + self.client.post.return_value.url, + files=None, + json={"name": "my-organization", "suspended": False, "metadata": {"labels": None, "annotations": None}}, + ) self.assertIsNotNone(result) def test_remove(self): - self.client.delete.return_value = self.mock_response( - '/v3/organizations/organization_id', - HTTPStatus.NO_CONTENT, - None) - self.client.v3.organizations.remove('organization_id') + self.client.delete.return_value = self.mock_response("/v3/organizations/organization_id", HTTPStatus.NO_CONTENT, None) + self.client.v3.organizations.remove("organization_id") self.client.delete.assert_called_with(self.client.delete.return_value.url) def test_assign_default_isolation_segment(self): self.client.patch.return_value = self.mock_response( - '/v3/organizations/organization_id/relationships/default_isolation_segment', + "/v3/organizations/organization_id/relationships/default_isolation_segment", HTTPStatus.OK, None, - 'v3', 'organizations', 'PATCH_{id}_relationships_default_isolation_segment_response.json') - result = self.client.v3.organizations.assign_default_isolation_segment( - 'organization_id', 'iso_seg_guid') - self.client.patch.assert_called_with(self.client.patch.return_value.url, - json={'data': { - 'guid': 'iso_seg_guid' - } - }) + "v3", + "organizations", + "PATCH_{id}_relationships_default_isolation_segment_response.json", + ) + result = self.client.v3.organizations.assign_default_isolation_segment("organization_id", "iso_seg_guid") + self.client.patch.assert_called_with(self.client.patch.return_value.url, json={"data": {"guid": "iso_seg_guid"}}) self.assertIsNotNone(result) self.assertIsInstance(result, ToOneRelationship) self.assertEqual(result.guid, "9d8e007c-ce52-4ea7-8a57-f2825d2c6b39") def test_get_default_isolation_segment(self): self.client.get.return_value = self.mock_response( - '/v3/organizations/organization_id/relationships/default_isolation_segment', + "/v3/organizations/organization_id/relationships/default_isolation_segment", HTTPStatus.OK, None, - 'v3', 'organizations', - 'GET_{id}_relationships_default_isolation_segment_response.json') + "v3", + "organizations", + "GET_{id}_relationships_default_isolation_segment_response.json", + ) - result = self.client.v3.organizations.get_default_isolation_segment('organization_id') + result = self.client.v3.organizations.get_default_isolation_segment("organization_id") self.client.get.assert_called_with(self.client.get.return_value.url) self.assertIsInstance(result, ToOneRelationship) self.assertEqual(result.guid, "9d8e007c-ce52-4ea7-8a57-f2825d2c6b39") def test_get_default_domain(self): - self.client.get.return_value = self.mock_response('/v3/organizations/organization_id/domains/default', - HTTPStatus.OK, - None, - 'v3', 'organizations', - 'GET_{id}_default_domain_response.json') - default_domain = self.client.v3.organizations.get_default_domain('organization_id') + self.client.get.return_value = self.mock_response( + "/v3/organizations/organization_id/domains/default", + HTTPStatus.OK, + None, + "v3", + "organizations", + "GET_{id}_default_domain_response.json", + ) + default_domain = self.client.v3.organizations.get_default_domain("organization_id") self.client.get.assert_called_with(self.client.get.return_value.url) - self.assertEqual("test-domain.com", default_domain['name']) + self.assertEqual("test-domain.com", default_domain["name"]) self.assertIsInstance(default_domain, Entity) def test_get_usage_summary(self): - self.client.get.return_value = self.mock_response('/v3/organizations/organization_id/usage_summary', - HTTPStatus.OK, - None, - 'v3', 'organizations', 'GET_{id}_usage_summary_response.json') - self.client.v3.organizations.get_usage_summary('organization_id') + self.client.get.return_value = self.mock_response( + "/v3/organizations/organization_id/usage_summary", + HTTPStatus.OK, + None, + "v3", + "organizations", + "GET_{id}_usage_summary_response.json", + ) + self.client.v3.organizations.get_usage_summary("organization_id") self.client.get.assert_called_with(self.client.get.return_value.url) - @patch.object(sys, 'argv', ['main', 'list_organizations']) + @patch.object(sys, "argv", ["main", "list_organizations"]) def test_main_list_organizations(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = self.mock_response('/v3/organizations', - HTTPStatus.OK, - None, - 'v3', 'organizations', 'GET_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.get.return_value = self.mock_response( + "/v3/organizations", HTTPStatus.OK, None, "v3", "organizations", "GET_response.json" + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) - @patch.object(sys, 'argv', ['main', 'get_organization', '24637893-3b77-489d-bb79-8466f0d88b52']) + @patch.object(sys, "argv", ["main", "get_organization", "24637893-3b77-489d-bb79-8466f0d88b52"]) def test_main_get_organizations(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = self.mock_response('/v3/organizations/24637893-3b77-489d-bb79-8466f0d88b52', - HTTPStatus.OK, - None, - 'v3', 'organizations', 'GET_{id}_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.get.return_value = self.mock_response( + "/v3/organizations/24637893-3b77-489d-bb79-8466f0d88b52", + HTTPStatus.OK, + None, + "v3", + "organizations", + "GET_{id}_response.json", + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) diff --git a/test/v3/test_service_instances.py b/test/v3/test_service_instances.py index b8e1eb7..7d5c3e0 100644 --- a/test/v3/test_service_instances.py +++ b/test/v3/test_service_instances.py @@ -14,22 +14,20 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = self.mock_response('/v3/service_instances', - HTTPStatus.OK, - None, - 'v3', 'service_instances', 'GET_response.json') + self.client.get.return_value = self.mock_response( + "/v3/service_instances", HTTPStatus.OK, None, "v3", "service_instances", "GET_response.json" + ) all_service_instances = [service_instance for service_instance in self.client.v3.service_instances.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(1, len(all_service_instances)) - self.assertEqual(all_service_instances[0]['guid'], "85ccdcad-d725-4109-bca4-fd6ba062b5c8") + self.assertEqual(all_service_instances[0]["guid"], "85ccdcad-d725-4109-bca4-fd6ba062b5c8") self.assertIsInstance(all_service_instances[0], Entity) def test_get(self): - self.client.get.return_value = self.mock_response('/v3/service_instances/service_instance_id', - HTTPStatus.OK, - None, - 'v3', 'service_instances', 'GET_{id}_response.json') - service_instance = self.client.v3.service_instances.get('service_instance_id') + self.client.get.return_value = self.mock_response( + "/v3/service_instances/service_instance_id", HTTPStatus.OK, None, "v3", "service_instances", "GET_{id}_response.json" + ) + service_instance = self.client.v3.service_instances.get("service_instance_id") self.client.get.assert_called_with(self.client.get.return_value.url) - self.assertEqual("85ccdcad-d725-4109-bca4-fd6ba062b5c8", service_instance['guid']) + self.assertEqual("85ccdcad-d725-4109-bca4-fd6ba062b5c8", service_instance["guid"]) self.assertIsInstance(service_instance, Entity) diff --git a/test/v3/test_spaces.py b/test/v3/test_spaces.py index c6d8b2e..6a612f7 100644 --- a/test/v3/test_spaces.py +++ b/test/v3/test_spaces.py @@ -16,93 +16,79 @@ def setUp(self): def test_create(self): self.client.post.return_value = self.mock_response( - '/v3/spaces', - HTTPStatus.OK, - None, - 'v3', 'spaces', 'POST_response.json') - result = self.client.v3.spaces.create('space-name', 'organization-guid') - self.client.post.assert_called_with(self.client.post.return_value.url, - files=None, - json={'name': 'space-name', - 'relationships': { - 'organization': { - 'data': { - 'guid': 'organization-guid' - } - } - }}) + "/v3/spaces", HTTPStatus.OK, None, "v3", "spaces", "POST_response.json" + ) + result = self.client.v3.spaces.create("space-name", "organization-guid") + self.client.post.assert_called_with( + self.client.post.return_value.url, + files=None, + json={"name": "space-name", "relationships": {"organization": {"data": {"guid": "organization-guid"}}}}, + ) self.assertIsNotNone(result) self.assertIsInstance(result, Entity) def test_list(self): - self.client.get.return_value = self.mock_response('/v3/spaces', - HTTPStatus.OK, - None, - 'v3', 'spaces', 'GET_response.json') + self.client.get.return_value = self.mock_response("/v3/spaces", HTTPStatus.OK, None, "v3", "spaces", "GET_response.json") all_spaces = [space for space in self.client.v3.spaces.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(2, len(all_spaces)) - self.assertEqual(all_spaces[0]['name'], "space1") + self.assertEqual(all_spaces[0]["name"], "space1") self.assertIsInstance(all_spaces[0], Entity) def test_get(self): - self.client.get.return_value = self.mock_response('/v3/spaces/space_id', - HTTPStatus.OK, - None, - 'v3', 'spaces', 'GET_{id}_response.json') - space = self.client.v3.spaces.get('space_id') + self.client.get.return_value = self.mock_response( + "/v3/spaces/space_id", HTTPStatus.OK, None, "v3", "spaces", "GET_{id}_response.json" + ) + space = self.client.v3.spaces.get("space_id") self.client.get.assert_called_with(self.client.get.return_value.url) - self.assertEqual("my-space", space['name']) + self.assertEqual("my-space", space["name"]) self.assertIsInstance(space, Entity) def test_get_then_organization(self): - get_space = self.mock_response('/v3/spaces/space_id', HTTPStatus.OK, None, - 'v3', 'spaces', 'GET_{id}_response.json') - get_organization = self.mock_response('/v3/organizations/e00705b9-7b42-4561-ae97-2520399d2133', HTTPStatus.OK, - None, - 'v3', 'organizations', 'GET_{id}_response.json') - self.client.get.side_effect = [ - get_space, - get_organization - ] - organization = self.client.v3.spaces.get('space_id').organization() - self.client.get.assert_has_calls([call(get_space.url), - call(get_organization.url)], - any_order=False) - self.assertEqual("my-organization", organization['name']) + get_space = self.mock_response("/v3/spaces/space_id", HTTPStatus.OK, None, "v3", "spaces", "GET_{id}_response.json") + get_organization = self.mock_response( + "/v3/organizations/e00705b9-7b42-4561-ae97-2520399d2133", + HTTPStatus.OK, + None, + "v3", + "organizations", + "GET_{id}_response.json", + ) + self.client.get.side_effect = [get_space, get_organization] + organization = self.client.v3.spaces.get("space_id").organization() + self.client.get.assert_has_calls([call(get_space.url), call(get_organization.url)], any_order=False) + self.assertEqual("my-organization", organization["name"]) def test_get_assigned_isolation_segment(self): - self.client.get.return_value = self.mock_response('/v3/spaces/space_id/relationships/isolation_segment', - HTTPStatus.OK, - None, - 'v3', 'spaces', - 'GET_{id}_relationships_isolation_segment_response.json') + self.client.get.return_value = self.mock_response( + "/v3/spaces/space_id/relationships/isolation_segment", + HTTPStatus.OK, + None, + "v3", + "spaces", + "GET_{id}_relationships_isolation_segment_response.json", + ) - result = self.client.v3.spaces.get_assigned_isolation_segment('space_id') + result = self.client.v3.spaces.get_assigned_isolation_segment("space_id") self.assertIsInstance(result, ToOneRelationship) - self.assertEqual('e4c91047-3b29-4fda-b7f9-04033e5a9c9f', result.guid) + self.assertEqual("e4c91047-3b29-4fda-b7f9-04033e5a9c9f", result.guid) def test_assign_isolation_segment(self): - self.client.patch.return_value = self.mock_response('/v3/spaces/space_id/relationships/isolation_segment', - HTTPStatus.OK, - None, - 'v3', 'spaces', - 'POST_{id}_relationships_isolation_segment_response.json') - result = self.client.v3.spaces.assign_isolation_segment('space_id', 'iso-seg-guid') - self.client.patch.assert_called_with(self.client.patch.return_value.url, - json={ - "data": { - "guid": "iso-seg-guid" - } - }) + self.client.patch.return_value = self.mock_response( + "/v3/spaces/space_id/relationships/isolation_segment", + HTTPStatus.OK, + None, + "v3", + "spaces", + "POST_{id}_relationships_isolation_segment_response.json", + ) + result = self.client.v3.spaces.assign_isolation_segment("space_id", "iso-seg-guid") + self.client.patch.assert_called_with(self.client.patch.return_value.url, json={"data": {"guid": "iso-seg-guid"}}) self.assertIsInstance(result, ToOneRelationship) - self.assertEqual('iso-seg-guid', result.guid) + self.assertEqual("iso-seg-guid", result.guid) def test_remove(self): - self.client.delete.return_value = self.mock_response( - '/v3/spaces/space_id', - HTTPStatus.NO_CONTENT, - None) - self.client.v3.spaces.remove('space_id') + self.client.delete.return_value = self.mock_response("/v3/spaces/space_id", HTTPStatus.NO_CONTENT, None) + self.client.v3.spaces.remove("space_id") self.client.delete.assert_called_with(self.client.delete.return_value.url) diff --git a/test/v3/test_tasks.py b/test/v3/test_tasks.py index d1381b0..8f1a0cc 100644 --- a/test/v3/test_tasks.py +++ b/test/v3/test_tasks.py @@ -17,79 +17,73 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = self.mock_response('/v3/tasks', - HTTPStatus.OK, - None, - 'v3', 'tasks', 'GET_response.json') + self.client.get.return_value = self.mock_response("/v3/tasks", HTTPStatus.OK, None, "v3", "tasks", "GET_response.json") all_tasks = [task for task in self.client.v3.tasks.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(2, len(all_tasks)) - self.assertEqual(all_tasks[0]['name'], "hello") + self.assertEqual(all_tasks[0]["name"], "hello") self.assertIsInstance(all_tasks[0], Entity) def test_get(self): - self.client.get.return_value = self.mock_response('/v3/tasks/task_id', - HTTPStatus.OK, - None, - 'v3', 'tasks', 'GET_{id}_response.json') - task = self.client.v3.tasks.get('task_id') + self.client.get.return_value = self.mock_response( + "/v3/tasks/task_id", HTTPStatus.OK, None, "v3", "tasks", "GET_{id}_response.json" + ) + task = self.client.v3.tasks.get("task_id") self.client.get.assert_called_with(self.client.get.return_value.url) - self.assertEqual("migrate", task['name']) + self.assertEqual("migrate", task["name"]) self.assertIsInstance(task, Entity) def test_create(self): self.client.post.return_value = self.mock_response( - '/v3/apps/app_guid/tasks', - HTTPStatus.CREATED, - None, - 'v3', 'tasks', 'POST_response.json') - task = self.client.v3.tasks.create('app_guid', command='rake db:migrate') - self.client.post.assert_called_with(self.client.post.return_value.url, - files=None, - json=dict(command='rake db:migrate')) + "/v3/apps/app_guid/tasks", HTTPStatus.CREATED, None, "v3", "tasks", "POST_response.json" + ) + task = self.client.v3.tasks.create("app_guid", command="rake db:migrate") + self.client.post.assert_called_with(self.client.post.return_value.url, files=None, json=dict(command="rake db:migrate")) self.assertIsNotNone(task) def test_cancel(self): self.client.post.return_value = self.mock_response( - '/v3/tasks/task_guid/actions/cancel', + "/v3/tasks/task_guid/actions/cancel", HTTPStatus.ACCEPTED, None, - 'v3', 'tasks', 'POST_{id}_actions_cancel_response.json') - task = self.client.v3.tasks.cancel('task_guid') + "v3", + "tasks", + "POST_{id}_actions_cancel_response.json", + ) + task = self.client.v3.tasks.cancel("task_guid") self.client.post.assert_called_with(self.client.post.return_value.url, files=None, json=None) self.assertIsNotNone(task) - @patch.object(sys, 'argv', ['main', 'list_tasks', '-names', 'task_name']) + @patch.object(sys, "argv", ["main", "list_tasks", "-names", "task_name"]) def test_list_tasks(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.get.return_value = self.mock_response('/v3/tasks?names=task_name', - HTTPStatus.OK, - None, - 'v3', 'tasks', 'GET_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.get.return_value = self.mock_response( + "/v3/tasks?names=task_name", HTTPStatus.OK, None, "v3", "tasks", "GET_response.json" + ) main.main() self.client.get.assert_called_with(self.client.get.return_value.url) - @patch.object(sys, 'argv', ['main', 'create_task', 'app_id', '{"command": "rake db:migrate", "name": "example"}']) + @patch.object(sys, "argv", ["main", "create_task", "app_id", '{"command": "rake db:migrate", "name": "example"}']) def test_create_task(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.post.return_value = self.mock_response('/v3/apps/app_id/tasks', - HTTPStatus.CREATED, - None, - 'v3', 'tasks', 'POST_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.post.return_value = self.mock_response( + "/v3/apps/app_id/tasks", HTTPStatus.CREATED, None, "v3", "tasks", "POST_response.json" + ) main.main() - self.client.post.assert_called_with(self.client.post.return_value.url, - files=None, - json=dict(command='rake db:migrate', name='example')) + self.client.post.assert_called_with( + self.client.post.return_value.url, files=None, json=dict(command="rake db:migrate", name="example") + ) - @patch.object(sys, 'argv', ['main', 'cancel_task', 'task_id']) + @patch.object(sys, "argv", ["main", "cancel_task", "task_id"]) def test_cancel_task(self): - with patch('cloudfoundry_client.main.main.build_client_from_configuration', - new=lambda: self.client): - self.client.post.return_value = self.mock_response('/v3/tasks/task_id/actions/cancel', - HTTPStatus.CREATED, - None, - 'v3', 'tasks', 'POST_{id}_actions_cancel_response.json') + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.post.return_value = self.mock_response( + "/v3/tasks/task_id/actions/cancel", + HTTPStatus.CREATED, + None, + "v3", + "tasks", + "POST_{id}_actions_cancel_response.json", + ) main.main() self.client.post.assert_called_with(self.client.post.return_value.url, files=None, json=None) From 5db7c47331fdc290833dedf42ad474015f66c288 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sat, 20 Feb 2021 14:40:27 +0100 Subject: [PATCH 063/264] Fix lint errors --- main/cloudfoundry_client/v3/entities.py | 17 ++++++++++------- main/cloudfoundry_client/v3/jobs.py | 17 +++++++++++------ 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index 8519740..ed29f1b 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -92,13 +92,15 @@ def __init__(self, *guids: str): class EntityManager(object): - def __init__(self, target_endpoint: str, client: "CloudFoundryClient", entity_uri: str, entity_type: ENTITY_TYPE = Entity): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient", entity_uri: str, + entity_type: ENTITY_TYPE = Entity): self.target_endpoint = target_endpoint self.entity_uri = entity_uri self.client = client self.entity_type = entity_type - def _post(self, url: str, data: Optional[dict] = None, files: Any = None, entity_type: ENTITY_TYPE = None) -> Entity: + def _post(self, url: str, data: Optional[dict] = None, files: Any = None, + entity_type: ENTITY_TYPE = None) -> Entity: response = self.client.post(url, json=data, files=files) _logger.debug("POST - %s - %s", url, response.text) return self._read_response(response, entity_type) @@ -135,9 +137,9 @@ def _paginate(self, url_requested: str, entity_type: Optional[ENTITY_TYPE] = Non for resource in response_json["resources"]: yield self._entity(resource, entity_type) if ( - "next" not in response_json["pagination"] - or response_json["pagination"]["next"] is None - or response_json["pagination"]["next"].get("href") is None + "next" not in response_json["pagination"] + or response_json["pagination"]["next"] is None + or response_json["pagination"]["next"].get("href") is None ): break else: @@ -189,7 +191,7 @@ def _read_response(self, response: Response, entity_type: Optional[ENTITY_TYPE]) result['links']['job'] = { "href": response.headers['Location'], "method": "GET", - } + } return self._entity(result, entity_type) @@ -214,6 +216,7 @@ def _append_encoded_parameter(parameters: List[str], args: Tuple[str, Any]) -> L return parameters if len(kwargs) > 0: - return "%s?%s" % (url, "&".join(functools.reduce(_append_encoded_parameter, sorted(list(kwargs.items())), []))) + return "%s?%s" % ( + url, "&".join(functools.reduce(_append_encoded_parameter, sorted(list(kwargs.items())), []))) else: return url diff --git a/main/cloudfoundry_client/v3/jobs.py b/main/cloudfoundry_client/v3/jobs.py index 4df3339..783bd12 100644 --- a/main/cloudfoundry_client/v3/jobs.py +++ b/main/cloudfoundry_client/v3/jobs.py @@ -1,8 +1,13 @@ import types +from typing import TYPE_CHECKING + import polling2 from cloudfoundry_client.v3.entities import EntityManager, Entity +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + class JobTimeout(Exception): pass @@ -13,12 +18,12 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(JobManager, self).__init__(target_endpoint, client, "/v3/jobs") def wait_for_job_completion( - self, - job_guid: str, - step: int = 1, - step_function: types.FunctionType = lambda step: min(step + step, 60), - poll_forever: bool = False, - timeout: int = 600, + self, + job_guid: str, + step: int = 1, + step_function: types.FunctionType = lambda step: min(step + step, 60), + poll_forever: bool = False, + timeout: int = 600, ) -> Entity: try: return polling2.poll( From f1d10765087e6ebcb29e822bdae2613ee02c5a79 Mon Sep 17 00:00:00 2001 From: FloThinksPi Date: Tue, 23 Feb 2021 11:32:54 +0100 Subject: [PATCH 064/264] Make buildpack.upload synchronous by default --- main/cloudfoundry_client/v3/buildpacks.py | 11 +++- test/v3/test_buildpacks.py | 67 +++++++++++++++++++---- 2 files changed, 66 insertions(+), 12 deletions(-) diff --git a/main/cloudfoundry_client/v3/buildpacks.py b/main/cloudfoundry_client/v3/buildpacks.py index f89f043..6405425 100644 --- a/main/cloudfoundry_client/v3/buildpacks.py +++ b/main/cloudfoundry_client/v3/buildpacks.py @@ -54,5 +54,12 @@ def update( } return super(BuildpackManager, self)._update(buildpack_guid, data) - def upload(self, buildpack_guid: str, buildpack_zip: str) -> Entity: - return super(BuildpackManager, self)._upload_bits(buildpack_guid, buildpack_zip) + def upload(self, buildpack_guid: str, buildpack_zip: str, asynchronous: bool = False) -> Entity: + buildpack = super(BuildpackManager, self)._upload_bits(buildpack_guid, buildpack_zip) + if not asynchronous: + self.client.v3.jobs.wait_for_job_completion(buildpack.job()["guid"]) + buildpack_after_job = super(BuildpackManager, self).get(buildpack["guid"]) + buildpack_after_job["links"]["job"] = buildpack["links"]["job"] + buildpack_after_job.job = buildpack.job + return buildpack_after_job + return buildpack diff --git a/test/v3/test_buildpacks.py b/test/v3/test_buildpacks.py index 79fef46..7740779 100644 --- a/test/v3/test_buildpacks.py +++ b/test/v3/test_buildpacks.py @@ -101,18 +101,65 @@ def test_main_get_buildpack(self): def test_upload_buildpack(self): self.client.post.return_value = self.mock_response( - '/v3/buildpacks/buildpack_id/upload', + "/v3/buildpacks/buildpack_id/upload", HTTPStatus.ACCEPTED, - { - "Location": "https://somewhere.org/v3/jobs/job_guid" - }, - 'v3', 'buildpacks', 'POST_response.json') + {"Location": "https://somewhere.org/v3/jobs/job_id"}, + "v3", + "buildpacks", + "POST_response.json", + ) + + self.client.get.side_effect = [ + self.mock_response( + "/v3/jobs/job_id", + HTTPStatus.OK, + None, + "v3", + "jobs", + "GET_{id}_complete_response.json", + ), + self.mock_response( + "/v3/buildpacks/buildpack_id", + HTTPStatus.OK, + None, + "v3", + "buildpacks", + "GET_{id}_response.json", + ), + ] + + with patch("cloudfoundry_client.v3.entities.open", mock_open(read_data="ZipContent")) as m: + with patch("cloudfoundry_client.v3.jobs.JobManager.wait_for_job_completion") as job_mock: + result = self.client.v3.buildpacks.upload("buildpack_id", "/path/to/buildpack.zip") - with patch('cloudfoundry_client.v3.entities.open', mock_open(read_data='ZipContent')) as m: - result = self.client.v3.buildpacks.upload('buildpack_id', '/path/to/buildpack.zip',) + # Check that we made the post call to upload the buildpack + self.client.post.assert_called_with( + self.client.post.return_value.url, files={"bits": ("/path/to/buildpack.zip", m.return_value)}, json=None + ) + # Check that we wait for job completion + job_mock.assert_called_once() + # We are doing upload->waitForJob->getbuildpack to get a fresh buildpack entity after the job finished + # We then rewrite the job information into the new buildpack entity since it is missing on get endpoint + # So check job link and function is also in the returned entity when we waited for the job. + self.assertIsNotNone(result["links"]["job"]) + self.assertIsNotNone(result.job) - self.client.post.assert_called_with(self.client.post.return_value.url, - files={'bits': ('/path/to/buildpack.zip', m.return_value)}, - json=None) + def test_upload_buildpack_dont_wait_for_job_completion(self): + self.client.post.return_value = self.mock_response( + "/v3/buildpacks/buildpack_id/upload", + HTTPStatus.ACCEPTED, + {"Location": "https://somewhere.org/v3/jobs/job_guid"}, + "v3", + "buildpacks", + "POST_response.json", + ) + + with patch("cloudfoundry_client.v3.entities.open", mock_open(read_data="ZipContent")) as m: + result = self.client.v3.buildpacks.upload("buildpack_id", "/path/to/buildpack.zip", asynchronous=True) + + self.client.post.assert_called_with( + self.client.post.return_value.url, files={"bits": ("/path/to/buildpack.zip", m.return_value)}, json=None + ) + self.client.get.assert_not_called() self.assertIsNotNone(result) From 100517cf57a700a9c03b34cc607768cdc2d0a587 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Feb 2021 02:48:04 +0000 Subject: [PATCH 065/264] Bump aiohttp from 3.6.2 to 3.7.4 Bumps [aiohttp](https://github.com/aio-libs/aiohttp) from 3.6.2 to 3.7.4. - [Release notes](https://github.com/aio-libs/aiohttp/releases) - [Changelog](https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst) - [Commits](https://github.com/aio-libs/aiohttp/compare/v3.6.2...v3.7.4) Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8469553..3f5849c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -aiohttp==3.6.2 +aiohttp==3.7.4 protobuf==3.6.1 oauth2-client==1.2.1 websocket-client==0.54.0 From 09b36f1aa79a7f7dcc96af5c71dfc97cbef17910 Mon Sep 17 00:00:00 2001 From: Felipe Morais Date: Fri, 26 Feb 2021 14:01:53 -0300 Subject: [PATCH 066/264] Bumped PyYAML version from 5.3.1 to 5.4.1 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8469553..bf58b50 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ aiohttp==3.6.2 protobuf==3.6.1 oauth2-client==1.2.1 websocket-client==0.54.0 -PyYAML==5.3.1 +PyYAML==5.4.1 requests~=2.25.1 setuptools~=49.2.1 polling2==0.4.6 From 37a72859128a697d304a01388462a81a3738fc12 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sat, 27 Feb 2021 13:44:58 +0100 Subject: [PATCH 067/264] Fixes generated 'environment_variables' on V3 App entity Despite name ends with 's', 'environment_variables' link is a single entity instead of a paginate resource Close #115 --- main/cloudfoundry_client/v3/apps.py | 17 +++++++++++++++-- main/cloudfoundry_client/v3/domains.py | 4 ++-- main/cloudfoundry_client/v3/entities.py | 13 ++++++------- ...GET_{id}_environment_variables_response.json | 13 +++++++++++++ test/v3/test_apps.py | 17 +++++++++++++++-- 5 files changed, 51 insertions(+), 13 deletions(-) create mode 100644 test/fixtures/v3/apps/GET_{id}_environment_variables_response.json diff --git a/main/cloudfoundry_client/v3/apps.py b/main/cloudfoundry_client/v3/apps.py index ca041f5..a2a337d 100644 --- a/main/cloudfoundry_client/v3/apps.py +++ b/main/cloudfoundry_client/v3/apps.py @@ -1,15 +1,28 @@ +import functools from typing import TYPE_CHECKING from cloudfoundry_client.json_object import JsonObject -from cloudfoundry_client.v3.entities import EntityManager +from cloudfoundry_client.v3.entities import EntityManager, Entity if TYPE_CHECKING: from cloudfoundry_client.client import CloudFoundryClient +class App(Entity): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient", **kwargs): + super(App, self).__init__(target_endpoint, client, **kwargs) + # patch environment_variables method + environment_variables_link = self.get("links", {}).get('environment_variables', {}).get('href', None) + if environment_variables_link is not None: + other_manager = self._default_manager(client, target_endpoint) + new_method = functools.partial(other_manager._get, environment_variables_link) + new_method.__name__ = 'environment_variables' + setattr(self, 'environment_variables', new_method) + + class AppManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(AppManager, self).__init__(target_endpoint, client, "/v3/apps") + super(AppManager, self).__init__(target_endpoint, client, "/v3/apps", App) def remove(self, application_guid: str): super(AppManager, self)._remove(application_guid) diff --git a/main/cloudfoundry_client/v3/domains.py b/main/cloudfoundry_client/v3/domains.py index 084a7cf..51338c1 100644 --- a/main/cloudfoundry_client/v3/domains.py +++ b/main/cloudfoundry_client/v3/domains.py @@ -7,8 +7,8 @@ class Domain(Entity): - def __init__(self, target_endpoint: str, entity_manager: "EntityManager", **kwargs): - super(Domain, self).__init__(target_endpoint, entity_manager, **kwargs) + def __init__(self, target_endpoint: str, client: "CloudFoundryClient", **kwargs): + super(Domain, self).__init__(target_endpoint, client, **kwargs) relationships = self["relationships"] if "organization" in relationships: self["relationships"]["organization"] = ToOneRelationship.from_json_object(relationships["organization"]) diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index ed29f1b..3396e51 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -19,20 +19,15 @@ class Entity(JsonObject): def __init__(self, target_endpoint: str, client: "CloudFoundryClient", **kwargs): super(Entity, self).__init__(**kwargs) try: - def default_method(m, u): raise NotImplementedError("Unknown method %s for url %s" % (m, u)) - + default_manager = self._default_manager(client, target_endpoint) for link_name, link in self.get("links", {}).items(): if link_name != "self": link_method = link.get("method", "GET").lower() ref = link["href"] manager_name = link_name if link_name.endswith("s") else "%ss" % link_name - try: - other_manager = getattr(client.v3, manager_name) - except AttributeError: - # generic manager - other_manager = EntityManager(target_endpoint, client, "") + other_manager = getattr(client.v3, manager_name, default_manager) if link_method == "get": new_method = ( functools.partial(other_manager._paginate, ref) @@ -52,6 +47,10 @@ def default_method(m, u): except KeyError: raise InvalidEntity(**self) + @staticmethod + def _default_manager(client, target_endpoint): + return EntityManager(target_endpoint, client, "") + PaginateEntities = Generator[Entity, None, None] diff --git a/test/fixtures/v3/apps/GET_{id}_environment_variables_response.json b/test/fixtures/v3/apps/GET_{id}_environment_variables_response.json new file mode 100644 index 0000000..c0d016d --- /dev/null +++ b/test/fixtures/v3/apps/GET_{id}_environment_variables_response.json @@ -0,0 +1,13 @@ +{ + "var": { + "RAILS_ENV": "production" + }, + "links": { + "self": { + "href": "https://api.example.org/v3/apps/[guid]/environment_variables" + }, + "app": { + "href": "https://api.example.org/v3/apps/[guid]" + } + } +} \ No newline at end of file diff --git a/test/v3/test_apps.py b/test/v3/test_apps.py index c34f47a..6530269 100644 --- a/test/v3/test_apps.py +++ b/test/v3/test_apps.py @@ -15,7 +15,8 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = self.mock_response("/v3/apps", HTTPStatus.OK, None, "v3", "apps", "GET_response.json") + self.client.get.return_value = self.mock_response("/v3/apps", HTTPStatus.OK, None, "v3", "apps", + "GET_response.json") all_applications = [application for application in self.client.v3.apps.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(2, len(all_applications)) @@ -34,7 +35,8 @@ def test_get(self): def test_get_then_space(self): get_app = self.mock_response("/v3/apps/app_id", HTTPStatus.OK, None, "v3", "apps", "GET_{id}_response.json") get_space = self.mock_response( - "/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576", HTTPStatus.OK, None, "v3", "spaces", "GET_{id}_response.json" + "/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576", HTTPStatus.OK, None, "v3", "spaces", + "GET_{id}_response.json" ) self.client.get.side_effect = [get_app, get_space] space = self.client.v3.apps.get("app_id").space() @@ -57,6 +59,17 @@ def test_get_then_start(self): self.assertEqual("my_app", app["name"]) self.assertIsInstance(app, Entity) + def test_get_then_environment_variables(self): + get_app = self.mock_response("/v3/apps/app_id", HTTPStatus.OK, None, "v3", "apps", "GET_{id}_response.json") + get_environment_variables = self.mock_response( + "/v3/apps/app_id/environment_variables", HTTPStatus.OK, None, "v3", "apps", + "GET_{id}_environment_variables_response.json" + ) + self.client.get.side_effect = [get_app, get_environment_variables] + environment_variables = self.client.v3.apps.get("app_id").environment_variables() + self.assertIsInstance(environment_variables, dict) + self.assertEqual("production", environment_variables["var"]["RAILS_ENV"]) + def test_remove(self): self.client.delete.return_value = self.mock_response("/v3/apps/app_id", HTTPStatus.NO_CONTENT, None) self.client.v3.apps.remove("app_id") From 508d759136e3892a263619f038f07e0dbb3c263c Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Mon, 1 Mar 2021 14:43:06 +0100 Subject: [PATCH 068/264] Prepare '1.16.0' version --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 8128f81..8f64e3d 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.15.0" +__version__ = "1.16.0" From 931f34de8d84825adc3cf1d40e37e2dd8a2f03a3 Mon Sep 17 00:00:00 2001 From: Almog Hamdani Date: Tue, 2 Mar 2021 12:32:25 +0200 Subject: [PATCH 069/264] Allow newer versions of protobuf --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 186fc00..6e7f0e2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ aiohttp==3.7.4 -protobuf==3.6.1 +protobuf>=3.6.1 oauth2-client==1.2.1 websocket-client==0.54.0 PyYAML==5.4.1 From 07efa43e9bf154e1d4c3aca80cce9be21db50579 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sun, 7 Mar 2021 19:33:12 +0100 Subject: [PATCH 070/264] Clean bad versions in requirements.txt Remove setuptools from dependency list See #120 --- requirements.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 186fc00..460b59c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,5 @@ protobuf==3.6.1 oauth2-client==1.2.1 websocket-client==0.54.0 PyYAML==5.4.1 -requests~=2.25.1 -setuptools~=49.2.1 +requests>=2.5.0 polling2==0.4.6 From 66599b658bc1f502b6e389f51607c53f678683cf Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Fri, 12 Mar 2021 21:02:04 +0100 Subject: [PATCH 071/264] Prepare '1.16.1' version --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 8f64e3d..f59c4c9 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.16.0" +__version__ = "1.16.1" From fc2e12c52d929eb0bea403208c00d47d7b3c83cc Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Fri, 12 Mar 2021 21:11:05 +0100 Subject: [PATCH 072/264] [ci/cd] added 'pull_request' for CI action --- .github/workflows/pythonpackage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index f3deb76..b51a9ae 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -1,6 +1,6 @@ name: Python package -on: [push] +on: [push, pull_request] jobs: build: From 7849de87423fc8ac58dfe5b5a9ac4192236894e5 Mon Sep 17 00:00:00 2001 From: Jakob Schmid Date: Mon, 22 Mar 2021 14:20:15 +0100 Subject: [PATCH 073/264] Add synchronous remove for service_instances --- main/cloudfoundry_client/client.py | 4 ++-- main/cloudfoundry_client/v3/entities.py | 16 ++++++++++++---- main/cloudfoundry_client/v3/service_instances.py | 14 ++++++++++++++ 3 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 main/cloudfoundry_client/v3/service_instances.py diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 91e26cb..0a1eaff 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -26,10 +26,10 @@ from cloudfoundry_client.v3.apps import AppManager as AppManagerV3 from cloudfoundry_client.v3.buildpacks import BuildpackManager as BuildpackManagerV3 from cloudfoundry_client.v3.domains import DomainManager -from cloudfoundry_client.v3.entities import EntityManager as EntityManagerV3 from cloudfoundry_client.v3.feature_flags import FeatureFlagManager from cloudfoundry_client.v3.isolation_segments import IsolationSegmentManager from cloudfoundry_client.v3.organizations import OrganizationManager +from cloudfoundry_client.v3.service_instances import ServiceInstanceManager as ServiceInstanceManagerV3 from cloudfoundry_client.v3.spaces import SpaceManager from cloudfoundry_client.v3.tasks import TaskManager from cloudfoundry_client.v3.jobs import JobManager as JobManagerV3 @@ -96,7 +96,7 @@ def __init__(self, target_endpoint: str, credential_manager: "CloudFoundryClient self.isolation_segments = IsolationSegmentManager(target_endpoint, credential_manager) self.spaces = SpaceManager(target_endpoint, credential_manager) self.organizations = OrganizationManager(target_endpoint, credential_manager) - self.service_instances = EntityManagerV3(target_endpoint, credential_manager, "/v3/service_instances") + self.service_instances = ServiceInstanceManagerV3(target_endpoint, credential_manager) self.tasks = TaskManager(target_endpoint, credential_manager) self.jobs = JobManagerV3(target_endpoint, credential_manager) diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index 3396e51..e006bfd 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -1,7 +1,7 @@ import functools import logging from typing import Any, Generator, Optional, List, Tuple, Union, TypeVar, TYPE_CHECKING -from urllib.parse import quote +from urllib.parse import quote, urlparse from requests import Response @@ -119,9 +119,13 @@ def _patch(self, url: str, data: dict, entity_type: Optional[ENTITY_TYPE] = None _logger.debug("PATCH - %s - %s", url, response.text) return self._read_response(response, entity_type) - def _delete(self, url: str): + def _delete(self, url: str, entity_type: Optional[ENTITY_TYPE] = None) -> Optional[JsonObject]: response = self.client.delete(url) _logger.debug("DELETE - %s - %s", url, response.text) + try: + return response.headers['Location'] + except (AttributeError, KeyError): + return None def _list(self, requested_path: str, entity_type: Optional[ENTITY_TYPE] = None, **kwargs) -> PaginateEntities: url_requested = EntityManager._get_url_filtered("%s%s" % (self.target_endpoint, requested_path), **kwargs) @@ -158,9 +162,13 @@ def _update(self, resource_id: str, data: dict) -> Entity: url = "%s%s/%s" % (self.target_endpoint, self.entity_uri, resource_id) return self._patch(url, data) - def _remove(self, resource_id: str): + def _remove(self, resource_id: str, asynchronous: bool = True): url = "%s%s/%s" % (self.target_endpoint, self.entity_uri, resource_id) - self._delete(url) + job_location = self._delete(url) + if not asynchronous and job_location is not None: + job_url = urlparse(job_location) + job_guid = job_url.path.rsplit("/", 1)[-1] + self.client.v3.jobs.wait_for_job_completion(job_guid) def __iter__(self) -> PaginateEntities: return self.list() diff --git a/main/cloudfoundry_client/v3/service_instances.py b/main/cloudfoundry_client/v3/service_instances.py new file mode 100644 index 0000000..676a6fa --- /dev/null +++ b/main/cloudfoundry_client/v3/service_instances.py @@ -0,0 +1,14 @@ +from typing import TYPE_CHECKING + +from cloudfoundry_client.v3.entities import EntityManager + +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + + +class ServiceInstanceManager(EntityManager): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(ServiceInstanceManager, self).__init__(target_endpoint, client, "/v3/service_instances") + + def remove(self, guid: str, asynchronous: bool = True): + super(ServiceInstanceManager, self)._remove(guid, asynchronous) From 07f9e7a55b532c48bcb6a9c6d60c9b5d596aa14d Mon Sep 17 00:00:00 2001 From: Jakob Schmid Date: Mon, 22 Mar 2021 17:12:54 +0100 Subject: [PATCH 074/264] Fix missing headers in MockResponse --- test/abstract_test_case.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/abstract_test_case.py b/test/abstract_test_case.py index 511f02f..13ac4ec 100644 --- a/test/abstract_test_case.py +++ b/test/abstract_test_case.py @@ -100,4 +100,4 @@ def mock_response(uri: str, status_code: HTTPStatus, headers: Optional[dict], *p headers=headers, ) else: - return MockResponse("%s%s" % (AbstractTestCase.TARGET_ENDPOINT, uri), status_code.value, "") + return MockResponse("%s%s" % (AbstractTestCase.TARGET_ENDPOINT, uri), status_code.value, "", headers) From fef8525343034190a7fc42827f2a4c679040c449 Mon Sep 17 00:00:00 2001 From: Jakob Schmid Date: Mon, 22 Mar 2021 17:14:00 +0100 Subject: [PATCH 075/264] Add tests for synchronous SI deletion --- test/v3/test_service_instances.py | 37 ++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/test/v3/test_service_instances.py b/test/v3/test_service_instances.py index 7d5c3e0..b82c3c4 100644 --- a/test/v3/test_service_instances.py +++ b/test/v3/test_service_instances.py @@ -1,6 +1,6 @@ import unittest from http import HTTPStatus - +from unittest.mock import patch from abstract_test_case import AbstractTestCase from cloudfoundry_client.v3.entities import Entity @@ -31,3 +31,38 @@ def test_get(self): self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual("85ccdcad-d725-4109-bca4-fd6ba062b5c8", service_instance["guid"]) self.assertIsInstance(service_instance, Entity) + + def test_remove_user_provided_service_instance(self): + self.client.delete.return_value = self.mock_response("/v3/service_instances/service_instance_id", HTTPStatus.NO_CONTENT, None) + self.client.v3.service_instances.remove("service_instance_id") + self.client.delete.assert_called_with(self.client.delete.return_value.url) + + def test_remove(self): + self.client.delete.return_value = self.mock_response("/v3/service_instances/service_instance_id", HTTPStatus.ACCEPTED, headers={"Location": "https://somewhere.org/v3/jobs/job_id"}) + self.client.v3.service_instances.remove("service_instance_id") + self.client.delete.assert_called_with(self.client.delete.return_value.url) + + @patch("time.sleep", return_value=None) + def test_remove_synchronous(self, sleepmock): + self.client.delete.return_value = self.mock_response("/v3/service_instances/service_instance_id", HTTPStatus.ACCEPTED, {"Location": "https://somewhere.org/v3/jobs/job_id"}) + self.client.get.side_effect = [ + self.mock_response( + "/v3/jobs/job_id", + HTTPStatus.OK, + None, + "v3", + "jobs", + "GET_{id}_processing_response.json", + ), + self.mock_response( + "/v3/jobs/job_id", + HTTPStatus.OK, + None, + "v3", + "jobs", + "GET_{id}_complete_response.json", + ), + ] + self.client.v3.service_instances.remove("service_instance_id",asynchronous=False) + self.client.delete.assert_called_with(self.client.delete.return_value.url) + assert self.client.get.call_count == 2 From f4abbe2f14b9ed9bb23690f0561223adc6027889 Mon Sep 17 00:00:00 2001 From: Jakob Schmid Date: Mon, 22 Mar 2021 17:58:36 +0100 Subject: [PATCH 076/264] Apply linters --- main/cloudfoundry_client/v3/entities.py | 2 +- test/v3/test_service_instances.py | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index e006bfd..7921b38 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -123,7 +123,7 @@ def _delete(self, url: str, entity_type: Optional[ENTITY_TYPE] = None) -> Option response = self.client.delete(url) _logger.debug("DELETE - %s - %s", url, response.text) try: - return response.headers['Location'] + return response.headers["Location"] except (AttributeError, KeyError): return None diff --git a/test/v3/test_service_instances.py b/test/v3/test_service_instances.py index b82c3c4..3669c41 100644 --- a/test/v3/test_service_instances.py +++ b/test/v3/test_service_instances.py @@ -33,18 +33,26 @@ def test_get(self): self.assertIsInstance(service_instance, Entity) def test_remove_user_provided_service_instance(self): - self.client.delete.return_value = self.mock_response("/v3/service_instances/service_instance_id", HTTPStatus.NO_CONTENT, None) + self.client.delete.return_value = self.mock_response( + "/v3/service_instances/service_instance_id", HTTPStatus.NO_CONTENT, None + ) self.client.v3.service_instances.remove("service_instance_id") self.client.delete.assert_called_with(self.client.delete.return_value.url) def test_remove(self): - self.client.delete.return_value = self.mock_response("/v3/service_instances/service_instance_id", HTTPStatus.ACCEPTED, headers={"Location": "https://somewhere.org/v3/jobs/job_id"}) + self.client.delete.return_value = self.mock_response( + "/v3/service_instances/service_instance_id", + HTTPStatus.ACCEPTED, + headers={"Location": "https://somewhere.org/v3/jobs/job_id"}, + ) self.client.v3.service_instances.remove("service_instance_id") self.client.delete.assert_called_with(self.client.delete.return_value.url) @patch("time.sleep", return_value=None) def test_remove_synchronous(self, sleepmock): - self.client.delete.return_value = self.mock_response("/v3/service_instances/service_instance_id", HTTPStatus.ACCEPTED, {"Location": "https://somewhere.org/v3/jobs/job_id"}) + self.client.delete.return_value = self.mock_response( + "/v3/service_instances/service_instance_id", HTTPStatus.ACCEPTED, {"Location": "https://somewhere.org/v3/jobs/job_id"} + ) self.client.get.side_effect = [ self.mock_response( "/v3/jobs/job_id", @@ -63,6 +71,6 @@ def test_remove_synchronous(self, sleepmock): "GET_{id}_complete_response.json", ), ] - self.client.v3.service_instances.remove("service_instance_id",asynchronous=False) + self.client.v3.service_instances.remove("service_instance_id", asynchronous=False) self.client.delete.assert_called_with(self.client.delete.return_value.url) assert self.client.get.call_count == 2 From b4558318996225dc082e158cc25f9c9f08d08819 Mon Sep 17 00:00:00 2001 From: Jakob Schmid Date: Tue, 20 Apr 2021 08:50:09 +0200 Subject: [PATCH 077/264] Remove unused parameter for _delete --- main/cloudfoundry_client/v3/entities.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index 7921b38..b2f0563 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -119,7 +119,7 @@ def _patch(self, url: str, data: dict, entity_type: Optional[ENTITY_TYPE] = None _logger.debug("PATCH - %s - %s", url, response.text) return self._read_response(response, entity_type) - def _delete(self, url: str, entity_type: Optional[ENTITY_TYPE] = None) -> Optional[JsonObject]: + def _delete(self, url: str) -> Optional[str]: response = self.client.delete(url) _logger.debug("DELETE - %s - %s", url, response.text) try: From 308e365a21811c225fa6efbb7fadb5401416a89a Mon Sep 17 00:00:00 2001 From: I505418 Date: Wed, 17 Mar 2021 17:07:19 +0100 Subject: [PATCH 078/264] Add Vcap-request-id to error message and debug logging + tests for logging and error message --- main/cloudfoundry_client/client.py | 14 ++++++- main/cloudfoundry_client/errors.py | 15 +++++--- test/test_client.py | 60 ++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 7 deletions(-) diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 0a1eaff..8712cb4 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -253,24 +253,36 @@ def _grant_client_credentials_request(self) -> dict: def get(self, url: str, params: Optional[dict] = None, **kwargs) -> Response: response = super(CloudFoundryClient, self).get(url, params, **kwargs) + CloudFoundryClient._log_request("GET", url, response) return CloudFoundryClient._check_response(response) def post(self, url: str, data=None, json=None, **kwargs) -> Response: response = super(CloudFoundryClient, self).post(url, data, json, **kwargs) + CloudFoundryClient._log_request("POST", url, response) return CloudFoundryClient._check_response(response) def put(self, url: str, data=None, json=None, **kwargs) -> Response: response = super(CloudFoundryClient, self).put(url, data, json, **kwargs) + CloudFoundryClient._log_request("PUT", url, response) return CloudFoundryClient._check_response(response) def patch(self, url: str, data=None, json=None, **kwargs) -> Response: response = super(CloudFoundryClient, self).patch(url, data, json, **kwargs) + CloudFoundryClient._log_request("PATCH", url, response) return CloudFoundryClient._check_response(response) def delete(self, url: str, **kwargs) -> Response: response = super(CloudFoundryClient, self).delete(url, **kwargs) + CloudFoundryClient._log_request("DELETE", url, response) return CloudFoundryClient._check_response(response) + @staticmethod + def _log_request(method: str, url: str, response: Response): + _logger.debug( + f"{method}: url={url} - response={response.text} - vcap-request-id=" + f"{response.headers.get('x-vcap-request-id', 'N/A')}" + ) + @staticmethod def _check_response(response: Response) -> Response: if int(response.status_code / 100) == 2: @@ -280,4 +292,4 @@ def _check_response(response: Response) -> Response: body = response.json() except ValueError: body = response.text - raise InvalidStatusCode(HTTPStatus(response.status_code), body) + raise InvalidStatusCode(HTTPStatus(response.status_code), body, response.headers.get("x-vcap-request-id", None)) diff --git a/main/cloudfoundry_client/errors.py b/main/cloudfoundry_client/errors.py index ef71f3f..888fe28 100644 --- a/main/cloudfoundry_client/errors.py +++ b/main/cloudfoundry_client/errors.py @@ -7,17 +7,20 @@ class InvalidLogResponseException(Exception): class InvalidStatusCode(Exception): - def __init__(self, status_code: HTTPStatus, body): + def __init__(self, status_code: HTTPStatus, body, request_id=None): self.status_code = status_code self.body = body + self.request_id = request_id def __str__(self): - if self.body is None: - return self.status_code.name - elif type(self.body) == str: - return "%s : %s" % (self.status_code.name, self.body) + error_message = self.status_code.name + if type(self.body) == str: + error_message += f" = {self.body}" else: - return "%s : %s" % (self.status_code.name, json.dumps(self.body)) + error_message += f" = {json.dumps(self.body)}" + if self.request_id: + error_message += f" - vcap-request-id = {self.request_id}" + return error_message class InvalidEntity(Exception): diff --git a/test/test_client.py b/test/test_client.py index 2d73c1f..9dd5461 100644 --- a/test/test_client.py +++ b/test/test_client.py @@ -4,6 +4,8 @@ from unittest.mock import patch from urllib.parse import quote +from cloudfoundry_client.errors import InvalidStatusCode + from abstract_test_case import AbstractTestCase from cloudfoundry_client.client import CloudFoundryClient from fake_requests import MockResponse, MockSession, FakeRequests @@ -153,3 +155,61 @@ def test_invalid_token_v2(self): response = MockResponse("http://some-cf-url", 401, text=json.dumps(dict(code=1000, error_code="CF-InvalidAuthToken"))) result = CloudFoundryClient._is_token_expired(response) self.assertTrue(result) + + def test_log_request(self): + response = MockResponse( + "http://some-cf-url", + 200, + text=json.dumps(dict(entity="entityTest", metadata="metadataTest")), + headers={"x-vcap-request-id": "testVcap"}, + ) + with self.assertLogs(level="DEBUG") as cm: + CloudFoundryClient._log_request("GET", "testURL", response) + self.assertEqual( + cm.output, + [ + "DEBUG:cloudfoundry_client.client:GET: url=testURL - response=" + '{"entity": "entityTest", "metadata": "metadataTest"} - vcap-request-id=testVcap' + ], + ) + + def test_log_request_empty_headers(self): + response = MockResponse("http://some-cf-url", 200, text=json.dumps(dict(entity="entityTest", metadata="metadataTest"))) + with self.assertLogs(level="DEBUG") as cm: + CloudFoundryClient._log_request("GET", "testURL", response) + self.assertEqual( + cm.output, + [ + "DEBUG:cloudfoundry_client.client:GET: url=testURL - response=" + '{"entity": "entityTest", "metadata": "metadataTest"} - vcap-request-id=N/A' + ], + ) + + def test_check_response_500_without_vcap(self): + response = MockResponse("http://some-cf-url", 500, text=json.dumps(dict(entity="entityTest", metadata="metadataTest"))) + with self.assertRaises(InvalidStatusCode): + CloudFoundryClient._check_response(response) + + def test_check_response_500_with_vcap(self): + response = MockResponse( + "http://some-cf-url", + 500, + text=json.dumps(dict(entity="entityTest", metadata="metadataTest")), + headers={"x-vcap-request-id": "testVcap"}, + ) + with self.assertRaisesRegex(InvalidStatusCode, "testVcap"): + CloudFoundryClient._check_response(response) + + def test_check_response_500_text(self): + response = MockResponse("http://some-cf-url", 500, text="This is test text") + with self.assertRaisesRegex(InvalidStatusCode, "This is test text"): + CloudFoundryClient._check_response(response) + + def test_check_response_500_json(self): + response = MockResponse("http://some-cf-url", 500, text=json.dumps(dict(entity="entityTest", metadata="metadataTest"))) + with self.assertRaisesRegex(InvalidStatusCode, "metadataTest"): + CloudFoundryClient._check_response(response) + + def test_check_response_200(self): + response = MockResponse("http://some-cf-url", 200, text=json.dumps(dict(entity="entityTest", metadata="metadataTest"))) + self.assertIsNotNone(CloudFoundryClient._check_response(response)) From 6a803fd0c26aac8d4a3f05637cd0e0fdb7933b21 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Wed, 21 Apr 2021 20:46:00 +0200 Subject: [PATCH 079/264] [refactorings] Log only one line per requests --- main/cloudfoundry_client/client.py | 4 ++-- main/cloudfoundry_client/v2/entities.py | 20 ++++++-------------- main/cloudfoundry_client/v3/entities.py | 9 --------- test/test_client.py | 8 ++++---- 4 files changed, 12 insertions(+), 29 deletions(-) diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 8712cb4..92b31a3 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -279,8 +279,8 @@ def delete(self, url: str, **kwargs) -> Response: @staticmethod def _log_request(method: str, url: str, response: Response): _logger.debug( - f"{method}: url={url} - response={response.text} - vcap-request-id=" - f"{response.headers.get('x-vcap-request-id', 'N/A')}" + f"{method}: url={url} - status_code={response.status_code}" + f" - vcap-request-id={response.headers.get('x-vcap-request-id', 'N/A')} - response={response.text}" ) @staticmethod diff --git a/main/cloudfoundry_client/v2/entities.py b/main/cloudfoundry_client/v2/entities.py index 710de5d..fbc7af7 100644 --- a/main/cloudfoundry_client/v2/entities.py +++ b/main/cloudfoundry_client/v2/entities.py @@ -1,4 +1,3 @@ -import logging from functools import partial, reduce from typing import Callable, List, Tuple, Any, Optional, Generator, TYPE_CHECKING from urllib.parse import quote @@ -11,8 +10,6 @@ if TYPE_CHECKING: from cloudfoundry_client.client import CloudFoundryClient -_logger = logging.getLogger(__name__) - class Entity(JsonObject): def __init__(self, target_endpoint: str, client: "CloudFoundryClient", *args, **kwargs): @@ -63,18 +60,11 @@ def __init__( entity_builder if entity_builder is not None else lambda pairs: Entity(target_endpoint, client, pairs) ) - def _get(self, requested_path: str, entity_builder: Optional[EntityBuilder] = None) -> Entity: - url = "%s%s" % (self.target_endpoint, requested_path) - response = self.client.get(url) - _logger.debug("GET - %s - %s", requested_path, response.text) - return self._read_response(response, entity_builder) - def _list(self, requested_path: str, entity_builder: Optional[EntityBuilder] = None, **kwargs) -> PaginateEntities: url_requested = self._get_url_filtered("%s%s" % (self.target_endpoint, requested_path), **kwargs) response = self.client.get(url_requested) entity_builder = self._get_entity_builder(entity_builder) while True: - _logger.debug("GET - %s - %s", url_requested, response.text) response_json = self._read_response(response, JsonObject) for resource in response_json["resources"]: yield entity_builder(list(resource.items())) @@ -96,19 +86,21 @@ def _remove(self, resource_id: str, **kwargs): url = "%s%s/%s" % (self.target_endpoint, self.entity_uri, resource_id) self._delete(url, **kwargs) + def _get(self, requested_path: str, entity_builder: Optional[EntityBuilder] = None) -> Entity: + url = "%s%s" % (self.target_endpoint, requested_path) + response = self.client.get(url) + return self._read_response(response, entity_builder) + def _post(self, url: str, data: Optional[dict] = None, **kwargs): response = self.client.post(url, json=data, **kwargs) - _logger.debug("POST - %s - %s", url, response.text) return self._read_response(response) def _put(self, url: str, data: Optional[dict] = None, **kwargs): response = self.client.put(url, json=data, **kwargs) - _logger.debug("PUT - %s - %s", url, response.text) return self._read_response(response) def _delete(self, url: str, **kwargs): - response = self.client.delete(url, **kwargs) - _logger.debug("DELETE - %s - %s", url, response.text) + self.client.delete(url, **kwargs) def __iter__(self) -> PaginateEntities: return self.list() diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index b2f0563..d5e255f 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -1,5 +1,4 @@ import functools -import logging from typing import Any, Generator, Optional, List, Tuple, Union, TypeVar, TYPE_CHECKING from urllib.parse import quote, urlparse @@ -12,8 +11,6 @@ if TYPE_CHECKING: from cloudfoundry_client.client import CloudFoundryClient -_logger = logging.getLogger(__name__) - class Entity(JsonObject): def __init__(self, target_endpoint: str, client: "CloudFoundryClient", **kwargs): @@ -101,27 +98,22 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient", entity_ur def _post(self, url: str, data: Optional[dict] = None, files: Any = None, entity_type: ENTITY_TYPE = None) -> Entity: response = self.client.post(url, json=data, files=files) - _logger.debug("POST - %s - %s", url, response.text) return self._read_response(response, entity_type) def _get(self, url: str, entity_type: Optional[ENTITY_TYPE] = None) -> Entity: response = self.client.get(url) - _logger.debug("GET - %s - %s", url, response.text) return self._read_response(response, entity_type) def _put(self, url: str, data: dict, entity_type: Optional[ENTITY_TYPE] = None) -> Entity: response = self.client.put(url, json=data) - _logger.debug("PUT - %s - %s", url, response.text) return self._read_response(response, entity_type) def _patch(self, url: str, data: dict, entity_type: Optional[ENTITY_TYPE] = None) -> Entity: response = self.client.patch(url, json=data) - _logger.debug("PATCH - %s - %s", url, response.text) return self._read_response(response, entity_type) def _delete(self, url: str) -> Optional[str]: response = self.client.delete(url) - _logger.debug("DELETE - %s - %s", url, response.text) try: return response.headers["Location"] except (AttributeError, KeyError): @@ -135,7 +127,6 @@ def _list(self, requested_path: str, entity_type: Optional[ENTITY_TYPE] = None, def _paginate(self, url_requested: str, entity_type: Optional[ENTITY_TYPE] = None) -> PaginateEntities: response = self.client.get(url_requested) while True: - _logger.debug("GET - %s - %s", url_requested, response.text) response_json = self._read_response(response, JsonObject) for resource in response_json["resources"]: yield self._entity(resource, entity_type) diff --git a/test/test_client.py b/test/test_client.py index 9dd5461..e0a5e26 100644 --- a/test/test_client.py +++ b/test/test_client.py @@ -168,8 +168,8 @@ def test_log_request(self): self.assertEqual( cm.output, [ - "DEBUG:cloudfoundry_client.client:GET: url=testURL - response=" - '{"entity": "entityTest", "metadata": "metadataTest"} - vcap-request-id=testVcap' + "DEBUG:cloudfoundry_client.client:GET: url=testURL - status_code=200 - vcap-request-id=testVcap - response=" + '{"entity": "entityTest", "metadata": "metadataTest"}' ], ) @@ -180,8 +180,8 @@ def test_log_request_empty_headers(self): self.assertEqual( cm.output, [ - "DEBUG:cloudfoundry_client.client:GET: url=testURL - response=" - '{"entity": "entityTest", "metadata": "metadataTest"} - vcap-request-id=N/A' + "DEBUG:cloudfoundry_client.client:GET: url=testURL - status_code=200 - vcap-request-id=N/A - response=" + '{"entity": "entityTest", "metadata": "metadataTest"}' ], ) From f0677802e2f8bdba0fc2ee2285f8427aac19dad9 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Fri, 23 Apr 2021 07:49:27 +0200 Subject: [PATCH 080/264] Prepare version 1.17.0 --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index f59c4c9..60acb6f 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.16.1" +__version__ = "1.17.0" From 0b2e8989d947ba0484fe69d4ea7fe622378db472 Mon Sep 17 00:00:00 2001 From: Sven Krieger <37476281+svkrieger@users.noreply.github.com> Date: Thu, 27 May 2021 15:23:36 +0200 Subject: [PATCH 081/264] Retrieve UAA endpoint from root rather than /info --- main/cloudfoundry_client/client.py | 8 +------- test/abstract_test_case.py | 11 +---------- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 92b31a3..4b9aef4 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -174,12 +174,6 @@ def rlpgateway(self): @staticmethod def _get_info(target_endpoint: str, proxy: Optional[dict] = None, verify: bool = True) -> Info: - info_response = CloudFoundryClient._check_response( - requests.get( - "%s/info" % target_endpoint, proxies=proxy if proxy is not None else dict(http="", https=""), verify=verify - ) - ) - info = info_response.json() root_response = CloudFoundryClient._check_response( requests.get("%s/" % target_endpoint, proxies=proxy if proxy is not None else dict(http="", https=""), verify=verify) ) @@ -190,7 +184,7 @@ def _get_info(target_endpoint: str, proxy: Optional[dict] = None, verify: bool = log_stream = root_links.get("log_stream") return Info( root_links["cloud_controller_v2"]["meta"]["version"], - info["authorization_endpoint"], + root_links["login"]["href"], target_endpoint, logging.get("href") if logging is not None else None, log_stream.get("href") if log_stream is not None else None, diff --git a/test/abstract_test_case.py b/test/abstract_test_case.py index 13ac4ec..a672e0a 100644 --- a/test/abstract_test_case.py +++ b/test/abstract_test_case.py @@ -45,16 +45,6 @@ def build_client(self): @staticmethod def _mock_info_calls(requests, with_doppler: bool = True, with_log_streams: bool = True): requests.get.side_effect = [ - MockResponse( - "%s/v2/info" % AbstractTestCase.TARGET_ENDPOINT, - status_code=HTTPStatus.OK.value, - text=json.dumps( - dict( - authorization_endpoint=AbstractTestCase.AUTHORIZATION_ENDPOINT, - token_endpoint=AbstractTestCase.TOKEN_ENDPOINT, - ) - ), - ), MockResponse( "%s/" % AbstractTestCase.TARGET_ENDPOINT, status_code=HTTPStatus.OK.value, @@ -74,6 +64,7 @@ def _mock_info_calls(requests, with_doppler: bool = True, with_log_streams: bool "log_stream": dict(href=AbstractTestCase.LOG_STREAM_ENDPOINT) if with_log_streams else None, "app_ssh": dict(href="ssh.nd-cfapi.itn.ftgroup:80"), "uaa": dict(href="https://uaa.nd-cfapi.itn.ftgroup"), + "login": dict(href=AbstractTestCase.AUTHORIZATION_ENDPOINT), "network_policy_v0": dict(href="https://api.nd-cfapi.itn.ftgroup/networking/v0/external"), "network_policy_v1": dict(href="https://api.nd-cfapi.itn.ftgroup/networking/v1/external"), } From 2fedf64048c7d53086a1c3bb89d57849b1373c99 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Mon, 31 May 2021 13:24:38 +0200 Subject: [PATCH 082/264] Prepare version 1.18.0 --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 60acb6f..0a2928f 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.17.0" +__version__ = "1.18.0" From 1f77100c75003ec9fef0fc7323d7ac369b2773f1 Mon Sep 17 00:00:00 2001 From: Will Gant Date: Tue, 1 Jun 2021 17:47:24 +0200 Subject: [PATCH 083/264] Implement service_brokers and service_plans for v3 endpoints This creates new functions for interacting with service_brokers and service_plans using the Cloud Controller's v3 API. We also extend the existing v3 implementation of service_instances (adding a missing 'create' function). In addition this commit changes v3/entities.py to make _read_response return a job ID for asynchronous operations that do not return a JSON object (e.g. updating a service broker). Co-authored-by: johha <45264872+johha@users.noreply.github.com> --- integration/{ => v2}/test_applications.py | 0 integration/{ => v2}/test_buildpacks.py | 0 integration/{ => v2}/test_loggregator.py | 0 integration/{ => v2}/test_navigation.py | 0 integration/{ => v2}/test_organizations.py | 0 integration/{ => v2}/test_routes.py | 0 integration/{ => v2}/test_service_bindings.py | 0 integration/{ => v2}/test_service_brokers.py | 0 .../{ => v2}/test_service_instances.py | 0 integration/{ => v2}/test_service_keys.py | 0 integration/{ => v2}/test_service_plans.py | 0 integration/{ => v2}/test_services.py | 0 integration/{ => v2}/test_spaces.py | 0 integration/v3/test_service_brokers.py | 18 +++ integration/v3/test_service_instances.py | 32 ++++ integration/v3/test_service_plans.py | 30 ++++ main/cloudfoundry_client/client.py | 37 +++-- main/cloudfoundry_client/v2/jobs.py | 2 +- .../cloudfoundry_client/v2/service_brokers.py | 10 +- .../v2/service_instances.py | 12 +- main/cloudfoundry_client/v2/service_plans.py | 4 +- main/cloudfoundry_client/v3/apps.py | 6 +- main/cloudfoundry_client/v3/entities.py | 33 ++-- main/cloudfoundry_client/v3/jobs.py | 12 +- .../cloudfoundry_client/v3/service_brokers.py | 63 ++++++++ .../v3/service_instances.py | 32 +++- main/cloudfoundry_client/v3/service_plans.py | 50 ++++++ .../v3/service_brokers/GET_response.json | 78 +++++++++ .../v3/service_brokers/GET_{id}_response.json | 31 ++++ .../service_brokers/PATCH_{id}_response.json | 31 ++++ .../v3/service_brokers/POST_response.json | 31 ++++ .../v3/service_plans/GET_response.json | 152 ++++++++++++++++++ .../v3/service_plans/GET_{id}_response.json | 78 +++++++++ .../GET_{id}_visibility_response.json | 3 + .../v3/service_plans/PATCH_{id}_response.json | 84 ++++++++++ ...id}_visibility_organizations_response.json | 13 ++ .../PATCH_{id}_visibility_type_response.json | 3 + .../POST_{id}_visibility_response.json | 8 + test/v3/test_apps.py | 14 +- test/v3/test_buildpacks.py | 6 +- test/v3/test_service_brokers.py | 111 +++++++++++++ test/v3/test_service_instances.py | 31 ++++ test/v3/test_service_plans.py | 127 +++++++++++++++ 43 files changed, 1078 insertions(+), 64 deletions(-) rename integration/{ => v2}/test_applications.py (100%) rename integration/{ => v2}/test_buildpacks.py (100%) rename integration/{ => v2}/test_loggregator.py (100%) rename integration/{ => v2}/test_navigation.py (100%) rename integration/{ => v2}/test_organizations.py (100%) rename integration/{ => v2}/test_routes.py (100%) rename integration/{ => v2}/test_service_bindings.py (100%) rename integration/{ => v2}/test_service_brokers.py (100%) rename integration/{ => v2}/test_service_instances.py (100%) rename integration/{ => v2}/test_service_keys.py (100%) rename integration/{ => v2}/test_service_plans.py (100%) rename integration/{ => v2}/test_services.py (100%) rename integration/{ => v2}/test_spaces.py (100%) create mode 100644 integration/v3/test_service_brokers.py create mode 100644 integration/v3/test_service_instances.py create mode 100644 integration/v3/test_service_plans.py create mode 100644 main/cloudfoundry_client/v3/service_brokers.py create mode 100644 main/cloudfoundry_client/v3/service_plans.py create mode 100644 test/fixtures/v3/service_brokers/GET_response.json create mode 100644 test/fixtures/v3/service_brokers/GET_{id}_response.json create mode 100644 test/fixtures/v3/service_brokers/PATCH_{id}_response.json create mode 100644 test/fixtures/v3/service_brokers/POST_response.json create mode 100644 test/fixtures/v3/service_plans/GET_response.json create mode 100644 test/fixtures/v3/service_plans/GET_{id}_response.json create mode 100644 test/fixtures/v3/service_plans/GET_{id}_visibility_response.json create mode 100644 test/fixtures/v3/service_plans/PATCH_{id}_response.json create mode 100644 test/fixtures/v3/service_plans/PATCH_{id}_visibility_organizations_response.json create mode 100644 test/fixtures/v3/service_plans/PATCH_{id}_visibility_type_response.json create mode 100644 test/fixtures/v3/service_plans/POST_{id}_visibility_response.json create mode 100644 test/v3/test_service_brokers.py create mode 100644 test/v3/test_service_plans.py diff --git a/integration/test_applications.py b/integration/v2/test_applications.py similarity index 100% rename from integration/test_applications.py rename to integration/v2/test_applications.py diff --git a/integration/test_buildpacks.py b/integration/v2/test_buildpacks.py similarity index 100% rename from integration/test_buildpacks.py rename to integration/v2/test_buildpacks.py diff --git a/integration/test_loggregator.py b/integration/v2/test_loggregator.py similarity index 100% rename from integration/test_loggregator.py rename to integration/v2/test_loggregator.py diff --git a/integration/test_navigation.py b/integration/v2/test_navigation.py similarity index 100% rename from integration/test_navigation.py rename to integration/v2/test_navigation.py diff --git a/integration/test_organizations.py b/integration/v2/test_organizations.py similarity index 100% rename from integration/test_organizations.py rename to integration/v2/test_organizations.py diff --git a/integration/test_routes.py b/integration/v2/test_routes.py similarity index 100% rename from integration/test_routes.py rename to integration/v2/test_routes.py diff --git a/integration/test_service_bindings.py b/integration/v2/test_service_bindings.py similarity index 100% rename from integration/test_service_bindings.py rename to integration/v2/test_service_bindings.py diff --git a/integration/test_service_brokers.py b/integration/v2/test_service_brokers.py similarity index 100% rename from integration/test_service_brokers.py rename to integration/v2/test_service_brokers.py diff --git a/integration/test_service_instances.py b/integration/v2/test_service_instances.py similarity index 100% rename from integration/test_service_instances.py rename to integration/v2/test_service_instances.py diff --git a/integration/test_service_keys.py b/integration/v2/test_service_keys.py similarity index 100% rename from integration/test_service_keys.py rename to integration/v2/test_service_keys.py diff --git a/integration/test_service_plans.py b/integration/v2/test_service_plans.py similarity index 100% rename from integration/test_service_plans.py rename to integration/v2/test_service_plans.py diff --git a/integration/test_services.py b/integration/v2/test_services.py similarity index 100% rename from integration/test_services.py rename to integration/v2/test_services.py diff --git a/integration/test_spaces.py b/integration/v2/test_spaces.py similarity index 100% rename from integration/test_spaces.py rename to integration/v2/test_spaces.py diff --git a/integration/v3/test_service_brokers.py b/integration/v3/test_service_brokers.py new file mode 100644 index 0000000..c407082 --- /dev/null +++ b/integration/v3/test_service_brokers.py @@ -0,0 +1,18 @@ +import logging +import unittest + +from config_test import build_client_from_configuration + +_logger = logging.getLogger(__name__) + + +class TestServiceBrokers(unittest.TestCase): + def test_list(self): + cpt = 0 + client = build_client_from_configuration() + for broker in client.v3.service_brokers.list(): + if cpt == 0: + self.assertIsNotNone(client.v3.service_brokers.get(broker["guid"])) + cpt += 1 + _logger.debug(broker) + _logger.debug("test broker list - %d found", cpt) diff --git a/integration/v3/test_service_instances.py b/integration/v3/test_service_instances.py new file mode 100644 index 0000000..623adec --- /dev/null +++ b/integration/v3/test_service_instances.py @@ -0,0 +1,32 @@ +import logging +import unittest + +from config_test import build_client_from_configuration + +_logger = logging.getLogger(__name__) + + +class TestServiceInstances(unittest.TestCase): + def test_create_delete(self): + client = build_client_from_configuration() + result = client.v3.service_instances.create( + space_guid=client.space_guid, + name="test_name", + service_plan_guid=client.plan_guid, + parameters=client.creation_parameters, + ) + client.v3.jobs.wait_for_job_completion(result.job()["guid"]) + service_instance_guid = client.v3.service_instances.get_first(names="test_name")["guid"] + client.v3.service_instances.remove(service_instance_guid) + + def test_get(self): + client = build_client_from_configuration() + cpt = 0 + for instance in client.v3.service_instances.list(): + if cpt == 0: + self.assertIsNotNone( + client.v3.service_instances.get_first(space_guids=instance["relationships"]["space"]["data"]["guid"]) + ) + self.assertIsNotNone(client.v3.service_instances.get(instance["guid"])) + cpt += 1 + _logger.debug("test_get - %d found", cpt) diff --git a/integration/v3/test_service_plans.py b/integration/v3/test_service_plans.py new file mode 100644 index 0000000..073316d --- /dev/null +++ b/integration/v3/test_service_plans.py @@ -0,0 +1,30 @@ +import logging +import unittest + +from config_test import build_client_from_configuration + +_logger = logging.getLogger(__name__) + + +class TestServicePlan(unittest.TestCase): + def test_list_instance_for_plan(self): + client = build_client_from_configuration() + for instance in client.v3.service_plans.list(service_offering_guids=client.plan_guid, space_guids=client.space_guid): + _logger.debug("test_list_instance_for_plan - %s -%s", instance["metadata"]["guid"], instance["entity"]["name"]) + + def test_list_by_broker(self): + cpt = 0 + client = build_client_from_configuration() + for plan in client.v3.service_plans.list(service_broker_guids=client.service_guid): + if cpt == 0: + _logger.debug(plan.json()) + cpt += 1 + _logger.debug("test plan list - %d found", cpt) + + def test_list(self): + cpt = 0 + client = build_client_from_configuration() + for plan in client.v3.service_plans.list(): + _logger.debug(plan.json()) + cpt += 1 + _logger.debug("test plan list - %d found", cpt) diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 92b31a3..30c1643 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -14,25 +14,28 @@ from cloudfoundry_client.v2.buildpacks import BuildpackManager as BuildpackManagerV2 from cloudfoundry_client.v2.entities import EntityManager as EntityManagerV2 from cloudfoundry_client.v2.events import EventManager -from cloudfoundry_client.v2.jobs import JobManager +from cloudfoundry_client.v2.jobs import JobManagerV2 from cloudfoundry_client.v2.resources import ResourceManager from cloudfoundry_client.v2.routes import RouteManager from cloudfoundry_client.v2.service_bindings import ServiceBindingManager -from cloudfoundry_client.v2.service_brokers import ServiceBrokerManager -from cloudfoundry_client.v2.service_instances import ServiceInstanceManager +from cloudfoundry_client.v2.service_brokers import ServiceBrokerManagerV2 +from cloudfoundry_client.v2.service_instances import ServiceInstanceManagerV2 from cloudfoundry_client.v2.service_keys import ServiceKeyManager from cloudfoundry_client.v2.service_plan_visibilities import ServicePlanVisibilityManager -from cloudfoundry_client.v2.service_plans import ServicePlanManager -from cloudfoundry_client.v3.apps import AppManager as AppManagerV3 -from cloudfoundry_client.v3.buildpacks import BuildpackManager as BuildpackManagerV3 +from cloudfoundry_client.v2.service_plans import ServicePlanManagerV2 + +from cloudfoundry_client.v3.service_brokers import ServiceBrokerManager +from cloudfoundry_client.v3.service_plans import ServicePlanManager +from cloudfoundry_client.v3.apps import AppManager +from cloudfoundry_client.v3.buildpacks import BuildpackManager from cloudfoundry_client.v3.domains import DomainManager from cloudfoundry_client.v3.feature_flags import FeatureFlagManager from cloudfoundry_client.v3.isolation_segments import IsolationSegmentManager from cloudfoundry_client.v3.organizations import OrganizationManager -from cloudfoundry_client.v3.service_instances import ServiceInstanceManager as ServiceInstanceManagerV3 +from cloudfoundry_client.v3.service_instances import ServiceInstanceManager from cloudfoundry_client.v3.spaces import SpaceManager from cloudfoundry_client.v3.tasks import TaskManager -from cloudfoundry_client.v3.jobs import JobManager as JobManagerV3 +from cloudfoundry_client.v3.jobs import JobManager _logger = logging.getLogger(__name__) @@ -62,13 +65,13 @@ class V2(object): def __init__(self, target_endpoint: str, credential_manager: "CloudFoundryClient"): self.apps = AppManagerV2(target_endpoint, credential_manager) self.buildpacks = BuildpackManagerV2(target_endpoint, credential_manager) - self.jobs = JobManager(target_endpoint, credential_manager) + self.jobs = JobManagerV2(target_endpoint, credential_manager) self.service_bindings = ServiceBindingManager(target_endpoint, credential_manager) - self.service_brokers = ServiceBrokerManager(target_endpoint, credential_manager) - self.service_instances = ServiceInstanceManager(target_endpoint, credential_manager) + self.service_brokers = ServiceBrokerManagerV2(target_endpoint, credential_manager) + self.service_instances = ServiceInstanceManagerV2(target_endpoint, credential_manager) self.service_keys = ServiceKeyManager(target_endpoint, credential_manager) self.service_plan_visibilities = ServicePlanVisibilityManager(target_endpoint, credential_manager) - self.service_plans = ServicePlanManager(target_endpoint, credential_manager) + self.service_plans = ServicePlanManagerV2(target_endpoint, credential_manager) # Default implementations self.event = EventManager(target_endpoint, credential_manager) self.organizations = EntityManagerV2(target_endpoint, credential_manager, "/v2/organizations") @@ -89,16 +92,18 @@ def __init__(self, target_endpoint: str, credential_manager: "CloudFoundryClient class V3(object): def __init__(self, target_endpoint: str, credential_manager: "CloudFoundryClient"): - self.apps = AppManagerV3(target_endpoint, credential_manager) - self.buildpacks = BuildpackManagerV3(target_endpoint, credential_manager) + self.apps = AppManager(target_endpoint, credential_manager) + self.buildpacks = BuildpackManager(target_endpoint, credential_manager) self.domains = DomainManager(target_endpoint, credential_manager) self.feature_flags = FeatureFlagManager(target_endpoint, credential_manager) self.isolation_segments = IsolationSegmentManager(target_endpoint, credential_manager) self.spaces = SpaceManager(target_endpoint, credential_manager) self.organizations = OrganizationManager(target_endpoint, credential_manager) - self.service_instances = ServiceInstanceManagerV3(target_endpoint, credential_manager) + self.service_instances = ServiceInstanceManager(target_endpoint, credential_manager) self.tasks = TaskManager(target_endpoint, credential_manager) - self.jobs = JobManagerV3(target_endpoint, credential_manager) + self.jobs = JobManager(target_endpoint, credential_manager) + self.service_brokers = ServiceBrokerManager(target_endpoint, credential_manager) + self.service_plans = ServicePlanManager(target_endpoint, credential_manager) class CloudFoundryClient(CredentialManager): diff --git a/main/cloudfoundry_client/v2/jobs.py b/main/cloudfoundry_client/v2/jobs.py index 0d6f30f..49c004d 100644 --- a/main/cloudfoundry_client/v2/jobs.py +++ b/main/cloudfoundry_client/v2/jobs.py @@ -6,7 +6,7 @@ from cloudfoundry_client.client import CloudFoundryClient -class JobManager(object): +class JobManagerV2(object): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): self.target_endpoint = target_endpoint self.client = client diff --git a/main/cloudfoundry_client/v2/service_brokers.py b/main/cloudfoundry_client/v2/service_brokers.py index abe44c7..9ebe46d 100644 --- a/main/cloudfoundry_client/v2/service_brokers.py +++ b/main/cloudfoundry_client/v2/service_brokers.py @@ -6,16 +6,16 @@ from cloudfoundry_client.client import CloudFoundryClient -class ServiceBrokerManager(EntityManager): +class ServiceBrokerManagerV2(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceBrokerManager, self).__init__(target_endpoint, client, "/v2/service_brokers") + super(ServiceBrokerManagerV2, self).__init__(target_endpoint, client, "/v2/service_brokers") def create( self, broker_url: str, broker_name: str, auth_username: str, auth_password: str, space_guid: Optional[str] = None ) -> Entity: request = self._request(broker_url=broker_url, name=broker_name, auth_username=auth_username, auth_password=auth_password) request["space_guid"] = space_guid - return super(ServiceBrokerManager, self)._create(request) + return super(ServiceBrokerManagerV2, self)._create(request) def update( self, @@ -30,7 +30,7 @@ def update( request["name"] = broker_name request["auth_username"] = auth_username request["auth_password"] = auth_password - return super(ServiceBrokerManager, self)._update(broker_guid, request) + return super(ServiceBrokerManagerV2, self)._update(broker_guid, request) def remove(self, broker_guid): - super(ServiceBrokerManager, self)._remove(broker_guid) + super(ServiceBrokerManagerV2, self)._remove(broker_guid) diff --git a/main/cloudfoundry_client/v2/service_instances.py b/main/cloudfoundry_client/v2/service_instances.py index f17fa7f..08674dc 100644 --- a/main/cloudfoundry_client/v2/service_instances.py +++ b/main/cloudfoundry_client/v2/service_instances.py @@ -6,11 +6,11 @@ from cloudfoundry_client.client import CloudFoundryClient -class ServiceInstanceManager(EntityManager): +class ServiceInstanceManagerV2(EntityManager): list_query_parameters = ["page", "results-per-page", "order-direction", "return_user_provided_service_instances"] def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceInstanceManager, self).__init__(target_endpoint, client, "/v2/service_instances") + super(ServiceInstanceManagerV2, self).__init__(target_endpoint, client, "/v2/service_instances") def create( self, @@ -25,7 +25,7 @@ def create( request["parameters"] = parameters request["tags"] = tags params = None if not accepts_incomplete else dict(accepts_incomplete="true") - return super(ServiceInstanceManager, self)._create(request, params=params) + return super(ServiceInstanceManagerV2, self)._create(request, params=params) def update( self, @@ -42,10 +42,10 @@ def update( request["parameters"] = parameters request["tags"] = tags params = None if not accepts_incomplete else dict(accepts_incomplete="true") - return super(ServiceInstanceManager, self)._update(instance_guid, request, params=params) + return super(ServiceInstanceManagerV2, self)._update(instance_guid, request, params=params) def list_permissions(self, instance_guid: str) -> Dict[str, bool]: - return super(ServiceInstanceManager, self)._get("%s/%s/permissions" % (self.entity_uri, instance_guid), dict) + return super(ServiceInstanceManagerV2, self)._get("%s/%s/permissions" % (self.entity_uri, instance_guid), dict) def remove(self, instance_guid: str, accepts_incomplete: Optional[bool] = False, purge: Optional[bool] = False): parameters = {} @@ -53,4 +53,4 @@ def remove(self, instance_guid: str, accepts_incomplete: Optional[bool] = False, parameters["accepts_incomplete"] = "true" if purge: parameters["purge"] = "true" - super(ServiceInstanceManager, self)._remove(instance_guid, params=parameters) + super(ServiceInstanceManagerV2, self)._remove(instance_guid, params=parameters) diff --git a/main/cloudfoundry_client/v2/service_plans.py b/main/cloudfoundry_client/v2/service_plans.py index 487b1cd..5d79619 100644 --- a/main/cloudfoundry_client/v2/service_plans.py +++ b/main/cloudfoundry_client/v2/service_plans.py @@ -6,9 +6,9 @@ from cloudfoundry_client.client import CloudFoundryClient -class ServicePlanManager(EntityManager): +class ServicePlanManagerV2(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServicePlanManager, self).__init__(target_endpoint, client, "/v2/service_plans") + super(ServicePlanManagerV2, self).__init__(target_endpoint, client, "/v2/service_plans") def create_from_resource_file(self, path: str) -> Entity: raise NotImplementedError("No creation allowed") diff --git a/main/cloudfoundry_client/v3/apps.py b/main/cloudfoundry_client/v3/apps.py index a2a337d..cd9ed0e 100644 --- a/main/cloudfoundry_client/v3/apps.py +++ b/main/cloudfoundry_client/v3/apps.py @@ -12,12 +12,12 @@ class App(Entity): def __init__(self, target_endpoint: str, client: "CloudFoundryClient", **kwargs): super(App, self).__init__(target_endpoint, client, **kwargs) # patch environment_variables method - environment_variables_link = self.get("links", {}).get('environment_variables', {}).get('href', None) + environment_variables_link = self.get("links", {}).get("environment_variables", {}).get("href", None) if environment_variables_link is not None: other_manager = self._default_manager(client, target_endpoint) new_method = functools.partial(other_manager._get, environment_variables_link) - new_method.__name__ = 'environment_variables' - setattr(self, 'environment_variables', new_method) + new_method.__name__ = "environment_variables" + setattr(self, "environment_variables", new_method) class AppManager(EntityManager): diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index d5e255f..3565e01 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -1,4 +1,5 @@ import functools +from json import JSONDecodeError from typing import Any, Generator, Optional, List, Tuple, Union, TypeVar, TYPE_CHECKING from urllib.parse import quote, urlparse @@ -16,8 +17,10 @@ class Entity(JsonObject): def __init__(self, target_endpoint: str, client: "CloudFoundryClient", **kwargs): super(Entity, self).__init__(**kwargs) try: + def default_method(m, u): raise NotImplementedError("Unknown method %s for url %s" % (m, u)) + default_manager = self._default_manager(client, target_endpoint) for link_name, link in self.get("links", {}).items(): if link_name != "self": @@ -88,15 +91,13 @@ def __init__(self, *guids: str): class EntityManager(object): - def __init__(self, target_endpoint: str, client: "CloudFoundryClient", entity_uri: str, - entity_type: ENTITY_TYPE = Entity): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient", entity_uri: str, entity_type: ENTITY_TYPE = Entity): self.target_endpoint = target_endpoint self.entity_uri = entity_uri self.client = client self.entity_type = entity_type - def _post(self, url: str, data: Optional[dict] = None, files: Any = None, - entity_type: ENTITY_TYPE = None) -> Entity: + def _post(self, url: str, data: Optional[dict] = None, files: Any = None, entity_type: ENTITY_TYPE = None) -> Entity: response = self.client.post(url, json=data, files=files) return self._read_response(response, entity_type) @@ -131,9 +132,9 @@ def _paginate(self, url_requested: str, entity_type: Optional[ENTITY_TYPE] = Non for resource in response_json["resources"]: yield self._entity(resource, entity_type) if ( - "next" not in response_json["pagination"] - or response_json["pagination"]["next"] is None - or response_json["pagination"]["next"].get("href") is None + "next" not in response_json["pagination"] + or response_json["pagination"]["next"] is None + or response_json["pagination"]["next"].get("href") is None ): break else: @@ -184,10 +185,15 @@ def get(self, entity_id: str, *extra_paths) -> Entity: return self._get(requested_path) def _read_response(self, response: Response, entity_type: Optional[ENTITY_TYPE]) -> Union[JsonObject, Entity]: - result = response.json(object_pairs_hook=JsonObject) - if 'Location' in response.headers: - result['links']['job'] = { - "href": response.headers['Location'], + try: + result = response.json(object_pairs_hook=JsonObject) + except JSONDecodeError: + # assume that response is empty + result = {"links": {}} + + if "Location" in response.headers: + result["links"]["job"] = { + "href": response.headers["Location"], "method": "GET", } @@ -198,7 +204,7 @@ def _request(**mandatory_parameters) -> Request: return Request(**mandatory_parameters) def _entity(self, result: JsonObject, entity_type: Optional[ENTITY_TYPE]) -> Union[JsonObject, Entity]: - if "guid" in result: + if "guid" in result or ("links" in result and "job" in result["links"]): return (entity_type or self.entity_type)(self.target_endpoint, self.client, **result) else: return result @@ -214,7 +220,6 @@ def _append_encoded_parameter(parameters: List[str], args: Tuple[str, Any]) -> L return parameters if len(kwargs) > 0: - return "%s?%s" % ( - url, "&".join(functools.reduce(_append_encoded_parameter, sorted(list(kwargs.items())), []))) + return "%s?%s" % (url, "&".join(functools.reduce(_append_encoded_parameter, sorted(list(kwargs.items())), []))) else: return url diff --git a/main/cloudfoundry_client/v3/jobs.py b/main/cloudfoundry_client/v3/jobs.py index 783bd12..554cb9e 100644 --- a/main/cloudfoundry_client/v3/jobs.py +++ b/main/cloudfoundry_client/v3/jobs.py @@ -18,12 +18,12 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(JobManager, self).__init__(target_endpoint, client, "/v3/jobs") def wait_for_job_completion( - self, - job_guid: str, - step: int = 1, - step_function: types.FunctionType = lambda step: min(step + step, 60), - poll_forever: bool = False, - timeout: int = 600, + self, + job_guid: str, + step: int = 1, + step_function: types.FunctionType = lambda step: min(step + step, 60), + poll_forever: bool = False, + timeout: int = 600, ) -> Entity: try: return polling2.poll( diff --git a/main/cloudfoundry_client/v3/service_brokers.py b/main/cloudfoundry_client/v3/service_brokers.py new file mode 100644 index 0000000..4c15886 --- /dev/null +++ b/main/cloudfoundry_client/v3/service_brokers.py @@ -0,0 +1,63 @@ +from typing import Optional, TYPE_CHECKING + +from cloudfoundry_client.v3.entities import EntityManager, Entity, ToOneRelationship + +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + + +class ServiceBrokerManager(EntityManager): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(ServiceBrokerManager, self).__init__(target_endpoint, client, "/v3/service_brokers") + + def create( + self, + name: str, + url: str, + auth_username: str, + auth_password: str, + space_guid: Optional[str] = None, + meta_labels: Optional[dict] = None, + meta_annotations: Optional[dict] = None, + ) -> Entity: + credentials = {"type": "basic", "credentials": {"username": auth_username, "password": auth_password}} + payload = dict(name=name, url=url, authentication=credentials) + if meta_labels or meta_annotations: + metadata = dict() + if meta_labels: + metadata["labels"] = meta_labels + if meta_annotations: + metadata["annotations"] = meta_annotations + payload["metadata"] = metadata + if space_guid: + payload["relationships"] = dict(space=ToOneRelationship(space_guid)) + return super(ServiceBrokerManager, self)._create(payload) + + def update( + self, + guid: str, + name: Optional[str] = None, + url: Optional[str] = None, + auth_username: Optional[str] = None, + auth_password: Optional[str] = None, + meta_labels: Optional[dict] = None, + meta_annotations: Optional[dict] = None, + ) -> Entity: + payload = dict() + if name: + payload["name"] = name + if url: + payload["url"] = url + if auth_username and auth_password: + payload["authentication"] = {"type": "basic", "credentials": {"username": auth_username, "password": auth_password}} + if meta_labels or meta_annotations: + metadata = dict() + if meta_labels: + metadata["labels"] = meta_labels + if meta_annotations: + metadata["annotations"] = meta_annotations + payload["metadata"] = metadata + return super(ServiceBrokerManager, self)._update(guid, payload) + + def remove(self, guid: str): + super(ServiceBrokerManager, self)._remove(guid) diff --git a/main/cloudfoundry_client/v3/service_instances.py b/main/cloudfoundry_client/v3/service_instances.py index 676a6fa..449a2ae 100644 --- a/main/cloudfoundry_client/v3/service_instances.py +++ b/main/cloudfoundry_client/v3/service_instances.py @@ -1,6 +1,6 @@ -from typing import TYPE_CHECKING +from typing import Optional, TYPE_CHECKING, List -from cloudfoundry_client.v3.entities import EntityManager +from cloudfoundry_client.v3.entities import Entity, EntityManager, ToOneRelationship if TYPE_CHECKING: from cloudfoundry_client.client import CloudFoundryClient @@ -10,5 +10,33 @@ class ServiceInstanceManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(ServiceInstanceManager, self).__init__(target_endpoint, client, "/v3/service_instances") + def create( + self, + name: str, + space_guid: str, + service_plan_guid: str, + meta_labels: Optional[dict] = None, + meta_annotations: Optional[dict] = None, + parameters: Optional[dict] = None, + tags: Optional[List[str]] = None, + ) -> Entity: + data = { + "name": name, + "type": "managed", + "relationships": {"space": ToOneRelationship(space_guid), "service_plan": ToOneRelationship(service_plan_guid)}, + } + if parameters: + data["parameters"] = parameters + if tags: + data["tags"] = tags + if meta_labels or meta_annotations: + metadata = dict() + if meta_labels: + metadata["labels"] = meta_labels + if meta_annotations: + metadata["annotations"] = meta_annotations + data["metadata"] = metadata + return super(ServiceInstanceManager, self)._create(data) + def remove(self, guid: str, asynchronous: bool = True): super(ServiceInstanceManager, self)._remove(guid, asynchronous) diff --git a/main/cloudfoundry_client/v3/service_plans.py b/main/cloudfoundry_client/v3/service_plans.py new file mode 100644 index 0000000..e80c266 --- /dev/null +++ b/main/cloudfoundry_client/v3/service_plans.py @@ -0,0 +1,50 @@ +from typing import Dict, List, Optional, TYPE_CHECKING + +from cloudfoundry_client.v3.entities import EntityManager, Entity + +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + + +class ServicePlanManager(EntityManager): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(ServicePlanManager, self).__init__(target_endpoint, client, "/v3/service_plans") + + def update( + self, + guid: str, + meta_labels: Optional[dict] = None, + meta_annotations: Optional[dict] = None, + ) -> Entity: + payload = {"metadata": {}} + + if meta_labels: + payload["metadata"]["labels"] = meta_labels + if meta_annotations: + payload["metadata"]["annotations"] = meta_annotations + return super(ServicePlanManager, self)._update(guid, payload) + + def remove(self, guid: str): + super(ServicePlanManager, self)._remove(guid) + + def get_visibility(self, service_plan_guid: str) -> Dict: + return super(ServicePlanManager, self)._get(f"{self.target_endpoint}{self.entity_uri}/{service_plan_guid}/visibility") + + def update_visibility(self, service_plan_guid: str, type: str, organizations: Optional[List[dict]] = None) -> Dict: + payload = {"type": type} + if organizations: + payload["organizations"] = organizations + return super(ServicePlanManager, self)._patch( + url=f"{self.target_endpoint}{self.entity_uri}/{service_plan_guid}/visibility", data=payload + ) + + def apply_visibility_to_extra_orgs(self, service_plan_guid: str, organizations: List[dict]) -> Dict: + payload = {"type": "organization", "organizations": organizations} + return super(ServicePlanManager, self)._post( + url=f"{self.target_endpoint}{self.entity_uri}/{service_plan_guid}/visibility", data=payload, files=None + ) + + def remove_org_from_service_plan_visibility(self, service_plan_guid: str, org_guid: str): + super(ServicePlanManager, self)._delete( + url=f"{self.target_endpoint}{self.entity_uri}/{service_plan_guid}/visibility/{org_guid}" + ) diff --git a/test/fixtures/v3/service_brokers/GET_response.json b/test/fixtures/v3/service_brokers/GET_response.json new file mode 100644 index 0000000..bfd4bb0 --- /dev/null +++ b/test/fixtures/v3/service_brokers/GET_response.json @@ -0,0 +1,78 @@ +{ + "pagination": { + "total_results": 2, + "total_pages": 1, + "first": { + "href": "http://somewhere.org/v3/service_brokers?page=1&per_page=1" + }, + "last": { + "href": "http://somewhere.org/v3/service_brokers?page=2&per_page=1" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "dde5ad2a-d8f4-44dc-a56f-0452d744f1c3", + "name": "my_service_broker", + "url": "https://example.service-broker.com", + "created_at": "2015-11-13T17:02:56Z", + "updated_at": "2016-06-08T16:41:26Z", + "relationships": { + "space": { + "data": { + "guid": "2f35885d-0c9d-4423-83ad-fd05066f8576" + } + } + }, + "metadata": { + "labels": { + "type": "dev" + }, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/service_brokers/dde5ad2a-d8f4-44dc-a56f-0452d744f1c3" + }, + "service_offerings": { + "href": "https://api.example.org/v3/service_offerings?service_broker_guids=dde5ad2a-d8f4-44dc-a56f-0452d744f1c3" + }, + "space": { + "href": "https://api.example.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576" + } + } + }, + { + "guid": "dde5ad2a-d8f4-44dc-a56f-0452d744f1c4", + "name": "my_service_broker2", + "url": "https://example.service-broker.com", + "created_at": "2015-11-13T17:02:56Z", + "updated_at": "2016-06-08T16:41:26Z", + "relationships": { + "space": { + "data": { + "guid": "2f35885d-0c9d-4423-83ad-fd05066f8576" + } + } + }, + "metadata": { + "labels": { + "type": "dev" + }, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/service_brokers/dde5ad2a-d8f4-44dc-a56f-0452d744f1c3" + }, + "service_offerings": { + "href": "https://api.example.org/v3/service_offerings?service_broker_guids=dde5ad2a-d8f4-44dc-a56f-0452d744f1c3" + }, + "space": { + "href": "https://api.example.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576" + } + } + } + ] +} \ No newline at end of file diff --git a/test/fixtures/v3/service_brokers/GET_{id}_response.json b/test/fixtures/v3/service_brokers/GET_{id}_response.json new file mode 100644 index 0000000..f6a7b1c --- /dev/null +++ b/test/fixtures/v3/service_brokers/GET_{id}_response.json @@ -0,0 +1,31 @@ +{ + "guid": "dde5ad2a-d8f4-44dc-a56f-0452d744f1c3", + "name": "my_service_broker", + "url": "https://example.service-broker.com", + "created_at": "2015-11-13T17:02:56Z", + "updated_at": "2016-06-08T16:41:26Z", + "relationships": { + "space": { + "data": { + "guid": "2f35885d-0c9d-4423-83ad-fd05066f8576" + } + } + }, + "metadata": { + "labels": { + "type": "dev" + }, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/service_brokers/dde5ad2a-d8f4-44dc-a56f-0452d744f1c3" + }, + "service_offerings": { + "href": "https://api.example.org/v3/service_offerings?service_broker_guids=dde5ad2a-d8f4-44dc-a56f-0452d744f1c3" + }, + "space": { + "href": "https://api.example.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576" + } + } +} \ No newline at end of file diff --git a/test/fixtures/v3/service_brokers/PATCH_{id}_response.json b/test/fixtures/v3/service_brokers/PATCH_{id}_response.json new file mode 100644 index 0000000..4414d34 --- /dev/null +++ b/test/fixtures/v3/service_brokers/PATCH_{id}_response.json @@ -0,0 +1,31 @@ +{ + "guid": "dde5ad2a-d8f4-44dc-a56f-0452d744f1c3", + "name": "my_service_broker", + "url": "https://example.service-broker.com", + "created_at": "2015-11-13T17:02:56Z", + "updated_at": "2016-06-08T16:41:26Z", + "relationships": { + "space": { + "data": { + "guid": "2f35885d-0c9d-4423-83ad-fd05066f8576" + } + } + }, + "metadata": { + "labels": { + "hello": "world" + }, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/service_brokers/dde5ad2a-d8f4-44dc-a56f-0452d744f1c3" + }, + "service_offerings": { + "href": "https://api.example.org/v3/service_offerings?service_broker_guids=dde5ad2a-d8f4-44dc-a56f-0452d744f1c3" + }, + "space": { + "href": "https://api.example.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576" + } + } +} \ No newline at end of file diff --git a/test/fixtures/v3/service_brokers/POST_response.json b/test/fixtures/v3/service_brokers/POST_response.json new file mode 100644 index 0000000..63f22a6 --- /dev/null +++ b/test/fixtures/v3/service_brokers/POST_response.json @@ -0,0 +1,31 @@ +{ + "guid": "dde5ad2a-d8f4-44dc-a56f-0452d744f1c3", + "name": "my_service_broker_renamed", + "url": "https://example.service-broker.com", + "created_at": "2015-11-13T17:02:56Z", + "updated_at": "2016-06-08T16:41:26Z", + "relationships": { + "space": { + "data": { + "guid": "2f35885d-0c9d-4423-83ad-fd05066f8576" + } + } + }, + "metadata": { + "labels": { + "type": "dev" + }, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/service_brokers/dde5ad2a-d8f4-44dc-a56f-0452d744f1c3" + }, + "service_offerings": { + "href": "https://api.example.org/v3/service_offerings?service_broker_guids=dde5ad2a-d8f4-44dc-a56f-0452d744f1c3" + }, + "space": { + "href": "https://api.example.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576" + } + } +} \ No newline at end of file diff --git a/test/fixtures/v3/service_plans/GET_response.json b/test/fixtures/v3/service_plans/GET_response.json new file mode 100644 index 0000000..267d524 --- /dev/null +++ b/test/fixtures/v3/service_plans/GET_response.json @@ -0,0 +1,152 @@ +{ + "pagination": { + "total_results": 3, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/service_plans?page=1&per_page=1" + }, + "last": { + "href": "https://api.example.org/v3/service_plans?page=2&per_page=1" + }, + "previous": null + }, + "resources": [ + { + "guid": "bf7eb420-11e5-11ea-b7db-4b5d5e7976a9", + "name": "my_big_service_plan", + "description": "Big plan", + "visibility_type": "organization", + "available": true, + "free": false, + "costs": [ + { + "currency": "USD", + "amount": 199.99, + "unit": "Monthly" + } + ], + "created_at": "2019-11-28T13:44:02Z", + "updated_at": "2019-11-28T13:44:02Z", + "maintenance_info": { + "version": "1.0.0+dev4", + "description": "Database version 7.8.0" + }, + "broker_catalog": { + "id": "db730a8c-11e5-11ea-838a-0f4fff3b1cfb", + "metadata": { + "custom-key": "custom-value" + }, + "maximum_polling_duration": null, + "features": { + "plan_updateable": true, + "bindable": true + } + }, + "schemas": { + "service_instance": { + "create": { + "parameters": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "billing-account": { + "description": "Billing account number used to charge use of shared fake server.", + "type": "string" + } + } + } + }, + "update": { + "parameters": {} + } + }, + "service_binding": { + "create": { + "parameters": {} + } + } + }, + "relationships": { + "service_offering": { + "data": { + "guid": "13c60e38-11e7-11ea-9106-33ee3c5bd4d7" + } + } + }, + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/service_plans/bf7eb420-11e5-11ea-b7db-4b5d5e7976a9" + }, + "service_offering": { + "href": "https://api.example.org/v3/service_offerings/13c60e38-11e7-11ea-9106-33ee3c5bd4d7" + }, + "visibility": { + "href": "https://api.example.org/v3/service_plans/bf7eb420-11e5-11ea-b7db-4b5d5e7976a9/visibility" + } + } + }, + { + "guid": "20e6cd62-12bb-11ea-90d1-7bfec2c75bcd", + "name": "other_service_plan", + "description": "Provides another service plan", + "visibility_type": "admin", + "available": true, + "free": true, + "created_at": "2019-11-29T16:44:02Z", + "updated_at": "2019-11-29T16:44:02Z", + "maintenance_info": {}, + "broker_catalog": { + "id": "3cb11822-12bb-11ea-beb1-a350dc7453b9", + "metadata": { + "other-data": true + }, + "maximum_polling_duration": null, + "features": { + "plan_updateable": true, + "bindable": true + } + }, + "schemas": { + "service_instance": { + "create": { + "parameters": {} + }, + "update": { + "parameters": {} + } + }, + "service_binding": { + "create": { + "parameters": {} + } + } + }, + "relationships": { + "service_offering": { + "data": { + "guid": "13c60e38-11e7-11ea-9106-33ee3c5bd4d7" + } + } + }, + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/service_plans/20e6cd62-12bb-11ea-90d1-7bfec2c75bcd" + }, + "service_offering": { + "href": "https://api.example.org/v3/service_offerings/13c60e38-11e7-11ea-9106-33ee3c5bd4d7" + }, + "visibility": { + "href": "https://api.example.org/v3/service_plans/20e6cd62-12bb-11ea-90d1-7bfec2c75bcd/visibility" + } + } + } + ] +} \ No newline at end of file diff --git a/test/fixtures/v3/service_plans/GET_{id}_response.json b/test/fixtures/v3/service_plans/GET_{id}_response.json new file mode 100644 index 0000000..a2473d9 --- /dev/null +++ b/test/fixtures/v3/service_plans/GET_{id}_response.json @@ -0,0 +1,78 @@ +{ + "guid": "bf7eb420-11e5-11ea-b7db-4b5d5e7976a9", + "name": "my_service_plan", + "description": "Big", + "visibility_type": "public", + "available": true, + "free": false, + "costs": [ + { + "currency": "USD", + "amount": 199.99, + "unit": "Monthly" + } + ], + "created_at": "2019-11-28T13:44:02Z", + "updated_at": "2019-11-28T13:44:02Z", + "maintenance_info": { + "version": "1.0.0+dev4", + "description": "Database version 7.8.0" + }, + "broker_catalog": { + "id": "db730a8c-11e5-11ea-838a-0f4fff3b1cfb", + "metadata": { + "custom-key": "custom-information" + }, + "maximum_polling_duration": null, + "features": { + "plan_updateable": true, + "bindable": true + } + }, + "schemas": { + "service_instance": { + "create": { + "parameters": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "billing-account": { + "description": "Billing account number used to charge use of shared fake server.", + "type": "string" + } + } + } + }, + "update": { + "parameters": {} + } + }, + "service_binding": { + "create": { + "parameters": {} + } + } + }, + "relationships": { + "service_offering": { + "data": { + "guid": "13c60e38-11e7-11ea-9106-33ee3c5bd4d7" + } + } + }, + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/service_plans/bf7eb420-11e5-11ea-b7db-4b5d5e7976a9" + }, + "service_offering": { + "href": "https://api.example.org/v3/service_offerings/13c60e38-11e7-11ea-9106-33ee3c5bd4d7" + }, + "visibility": { + "href": "https://api.example.org/v3/service_plans/bf7eb420-11e5-11ea-b7db-4b5d5e7976a9/visibility" + } + } +} \ No newline at end of file diff --git a/test/fixtures/v3/service_plans/GET_{id}_visibility_response.json b/test/fixtures/v3/service_plans/GET_{id}_visibility_response.json new file mode 100644 index 0000000..8c6f883 --- /dev/null +++ b/test/fixtures/v3/service_plans/GET_{id}_visibility_response.json @@ -0,0 +1,3 @@ +{ + "type": "public" +} \ No newline at end of file diff --git a/test/fixtures/v3/service_plans/PATCH_{id}_response.json b/test/fixtures/v3/service_plans/PATCH_{id}_response.json new file mode 100644 index 0000000..36e342f --- /dev/null +++ b/test/fixtures/v3/service_plans/PATCH_{id}_response.json @@ -0,0 +1,84 @@ +{ + "guid": "bf7eb420-11e5-11ea-b7db-4b5d5e7976a9", + "name": "my_service_plan", + "description": "Big", + "visibility_type": "public", + "available": true, + "free": false, + "costs": [ + { + "currency": "USD", + "amount": 199.99, + "unit": "Monthly" + } + ], + "created_at": "2019-11-28T13:44:02Z", + "updated_at": "2019-11-28T13:44:02Z", + "maintenance_info": { + "version": "1.0.0+dev4", + "description": "Database version 7.8.0" + }, + "broker_catalog": { + "id": "db730a8c-11e5-11ea-838a-0f4fff3b1cfb", + "metadata": { + "custom-key": "custom-information" + }, + "maximum_polling_duration": null, + "features": { + "plan_updateable": true, + "bindable": true + } + }, + "schemas": { + "service_instance": { + "create": { + "parameters": { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "billing-account": { + "description": "Billing account number used to charge use of shared fake server.", + "type": "string" + } + } + } + }, + "update": { + "parameters": {} + } + }, + "service_binding": { + "create": { + "parameters": {} + } + } + }, + "relationships": { + "service_offering": { + "data": { + "guid": "13c60e38-11e7-11ea-9106-33ee3c5bd4d7" + } + } + }, + "metadata": { + "labels": { + "hello": "world" + }, + "annotations": { + "annotations": { + "note": "detailed information" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/service_plans/bf7eb420-11e5-11ea-b7db-4b5d5e7976a9" + }, + "service_offering": { + "href": "https://api.example.org/v3/service_offerings/13c60e38-11e7-11ea-9106-33ee3c5bd4d7" + }, + "visibility": { + "href": "https://api.example.org/v3/service_plans/bf7eb420-11e5-11ea-b7db-4b5d5e7976a9/visibility" + } + } +} \ No newline at end of file diff --git a/test/fixtures/v3/service_plans/PATCH_{id}_visibility_organizations_response.json b/test/fixtures/v3/service_plans/PATCH_{id}_visibility_organizations_response.json new file mode 100644 index 0000000..dcdda7d --- /dev/null +++ b/test/fixtures/v3/service_plans/PATCH_{id}_visibility_organizations_response.json @@ -0,0 +1,13 @@ +{ + "type": "organization", + "organizations": [ + { + "guid": "0fc1ad4f-e1d7-4436-8e23-6b20f03c6482", + "name": "some_org" + }, + { + "guid": "0fc1ad4f-e1d7-4436-8e23-6b20f03c6483", + "name": "other_org" + } + ] +} \ No newline at end of file diff --git a/test/fixtures/v3/service_plans/PATCH_{id}_visibility_type_response.json b/test/fixtures/v3/service_plans/PATCH_{id}_visibility_type_response.json new file mode 100644 index 0000000..f0f8c79 --- /dev/null +++ b/test/fixtures/v3/service_plans/PATCH_{id}_visibility_type_response.json @@ -0,0 +1,3 @@ +{ + "type": "admin" +} \ No newline at end of file diff --git a/test/fixtures/v3/service_plans/POST_{id}_visibility_response.json b/test/fixtures/v3/service_plans/POST_{id}_visibility_response.json new file mode 100644 index 0000000..94c4fa4 --- /dev/null +++ b/test/fixtures/v3/service_plans/POST_{id}_visibility_response.json @@ -0,0 +1,8 @@ +{ + "organizations": [ + { + "guid": "b3af3658-d844-496a-8986-89b79a74c8ae" + } + ], + "type": "organization" + } \ No newline at end of file diff --git a/test/v3/test_apps.py b/test/v3/test_apps.py index 6530269..666cd28 100644 --- a/test/v3/test_apps.py +++ b/test/v3/test_apps.py @@ -15,8 +15,7 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = self.mock_response("/v3/apps", HTTPStatus.OK, None, "v3", "apps", - "GET_response.json") + self.client.get.return_value = self.mock_response("/v3/apps", HTTPStatus.OK, None, "v3", "apps", "GET_response.json") all_applications = [application for application in self.client.v3.apps.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(2, len(all_applications)) @@ -35,8 +34,7 @@ def test_get(self): def test_get_then_space(self): get_app = self.mock_response("/v3/apps/app_id", HTTPStatus.OK, None, "v3", "apps", "GET_{id}_response.json") get_space = self.mock_response( - "/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576", HTTPStatus.OK, None, "v3", "spaces", - "GET_{id}_response.json" + "/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576", HTTPStatus.OK, None, "v3", "spaces", "GET_{id}_response.json" ) self.client.get.side_effect = [get_app, get_space] space = self.client.v3.apps.get("app_id").space() @@ -62,8 +60,12 @@ def test_get_then_start(self): def test_get_then_environment_variables(self): get_app = self.mock_response("/v3/apps/app_id", HTTPStatus.OK, None, "v3", "apps", "GET_{id}_response.json") get_environment_variables = self.mock_response( - "/v3/apps/app_id/environment_variables", HTTPStatus.OK, None, "v3", "apps", - "GET_{id}_environment_variables_response.json" + "/v3/apps/app_id/environment_variables", + HTTPStatus.OK, + None, + "v3", + "apps", + "GET_{id}_environment_variables_response.json", ) self.client.get.side_effect = [get_app, get_environment_variables] environment_variables = self.client.v3.apps.get("app_id").environment_variables() diff --git a/test/v3/test_buildpacks.py b/test/v3/test_buildpacks.py index 7740779..d7d9ba5 100644 --- a/test/v3/test_buildpacks.py +++ b/test/v3/test_buildpacks.py @@ -103,7 +103,7 @@ def test_upload_buildpack(self): self.client.post.return_value = self.mock_response( "/v3/buildpacks/buildpack_id/upload", HTTPStatus.ACCEPTED, - {"Location": "https://somewhere.org/v3/jobs/job_id"}, + {"Location": "https://somewhere.org/v3/jobs/job_id", "Content-Type": "application/json"}, "v3", "buildpacks", "POST_response.json", @@ -113,7 +113,7 @@ def test_upload_buildpack(self): self.mock_response( "/v3/jobs/job_id", HTTPStatus.OK, - None, + {"Content-Type": "application/json"}, "v3", "jobs", "GET_{id}_complete_response.json", @@ -121,7 +121,7 @@ def test_upload_buildpack(self): self.mock_response( "/v3/buildpacks/buildpack_id", HTTPStatus.OK, - None, + {"Content-Type": "application/json"}, "v3", "buildpacks", "GET_{id}_response.json", diff --git a/test/v3/test_service_brokers.py b/test/v3/test_service_brokers.py new file mode 100644 index 0000000..fa4ecf3 --- /dev/null +++ b/test/v3/test_service_brokers.py @@ -0,0 +1,111 @@ +import unittest +from http import HTTPStatus + +from abstract_test_case import AbstractTestCase +from cloudfoundry_client.v3.entities import Entity + + +class TestServiceBrokers(unittest.TestCase, AbstractTestCase): + @classmethod + def setUpClass(cls): + cls.mock_client_class() + + def setUp(self): + self.build_client() + + def test_create(self): + self.client.post.return_value = self.mock_response( + "/v3/service_brokers", HTTPStatus.OK, None, "v3", "service_brokers", "POST_response.json" + ) + username = "us3rn4me" + password = "p4ssw0rd" + url = "https://example.service-broker.com" + result = self.client.v3.service_brokers.create("my_service_broker", url, username, password) + self.client.post.assert_called_with( + self.client.post.return_value.url, + files=None, + json={ + "name": "my_service_broker", + "url": url, + "authentication": {"type": "basic", "credentials": {"username": username, "password": password}}, + }, + ) + self.assertIsNotNone(result) + self.assertIsInstance(result, Entity) + + def test_create_with_space_guid(self): + self.client.post.return_value = self.mock_response( + "/v3/service_brokers", HTTPStatus.OK, None, "v3", "service_brokers", "POST_response.json" + ) + username = "us3rn4me" + password = "p4ssw0rd" + url = "https://example.service-broker.com" + result = self.client.v3.service_brokers.create("my_service_broker", url, username, password, "space-guid-123") + relationships = {"space": {"data": {"guid": "space-guid-123"}}} + self.client.post.assert_called_with( + self.client.post.return_value.url, + files=None, + json={ + "name": "my_service_broker", + "url": url, + "authentication": {"type": "basic", "credentials": {"username": username, "password": password}}, + "relationships": relationships, + }, + ) + self.assertIsNotNone(result) + self.assertIsInstance(result, Entity) + + def test_list(self): + self.client.get.return_value = self.mock_response( + "/v3/service_brokers", HTTPStatus.OK, None, "v3", "service_brokers", "GET_response.json" + ) + all_service_brokers = [service_broker for service_broker in self.client.v3.service_brokers.list()] + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual(2, len(all_service_brokers)) + self.assertEqual(all_service_brokers[0]["name"], "my_service_broker") + self.assertIsInstance(all_service_brokers[0], Entity) + + def test_get(self): + self.client.get.return_value = self.mock_response( + "/v3/service_brokers/service_broker_guid_123", HTTPStatus.OK, None, "v3", "service_brokers", "GET_{id}_response.json" + ) + service_broker = self.client.v3.service_brokers.get("service_broker_guid_123") + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual("my_service_broker", service_broker["name"]) + self.assertIsInstance(service_broker, Entity) + + def test_update_service_broker_metadata(self): + self.client.patch.return_value = self.mock_response( + "/v3/service_brokers/service_broker_guid_123", + HTTPStatus.OK, + None, + "v3", + "service_brokers", + "PATCH_{id}_response.json", + ) + service_broker = self.client.v3.service_brokers.update(guid="service_broker_guid_123", meta_labels={"hello": "world"}) + self.client.patch.assert_called_with( + self.client.patch.return_value.url, + json={"metadata": {"labels": {"hello": "world"}}}, + ) + self.assertIsNotNone(service_broker) + self.assertIsInstance(service_broker, Entity) + + def test_update_service_broker_name(self): + self.client.patch.return_value = self.mock_response( + "/v3/service_brokers/service_broker_guid_123", + HTTPStatus.OK, + headers={"Location": "http://localhost/v3/jobs/job-guid-123"}, + ) + service_broker = self.client.v3.service_brokers.update(guid="service_broker_guid_123", name="my_service_broker") + self.client.patch.assert_called_with( + self.client.patch.return_value.url, + json={"name": "my_service_broker"}, + ) + self.assertIsNotNone(service_broker) + self.assertIsInstance(service_broker, Entity) + + def test_remove(self): + self.client.delete.return_value = self.mock_response("/v3/service_brokers/service_broker_id", HTTPStatus.NO_CONTENT, None) + self.client.v3.service_brokers.remove("service_broker_id") + self.client.delete.assert_called_with(self.client.delete.return_value.url) diff --git a/test/v3/test_service_instances.py b/test/v3/test_service_instances.py index 3669c41..05615e1 100644 --- a/test/v3/test_service_instances.py +++ b/test/v3/test_service_instances.py @@ -1,6 +1,7 @@ import unittest from http import HTTPStatus from unittest.mock import patch + from abstract_test_case import AbstractTestCase from cloudfoundry_client.v3.entities import Entity @@ -13,6 +14,36 @@ def setUpClass(cls): def setUp(self): self.build_client() + def test_create(self): + self.client.post.return_value = self.mock_response( + "/v3/service_instances", + HTTPStatus.ACCEPTED, + headers={"Location": "https://somewhere.org/v3/jobs/job_id"}, + ) + result = self.client.v3.service_instances.create( + name="space-name", + parameters={"foo": "bar"}, + tags=["mytag", "myothertag"], + space_guid="space-guid-123", + service_plan_guid="service-plan-guid-123", + ) + self.client.post.assert_called_with( + self.client.post.return_value.url, + json={ + "name": "space-name", + "parameters": {"foo": "bar"}, + "tags": ["mytag", "myothertag"], + "type": "managed", + "relationships": { + "space": {"data": {"guid": "space-guid-123"}}, + "service_plan": {"data": {"guid": "service-plan-guid-123"}}, + }, + }, + files=None, + ) + self.assertIsNotNone(result) + self.assertIsInstance(result, Entity) + def test_list(self): self.client.get.return_value = self.mock_response( "/v3/service_instances", HTTPStatus.OK, None, "v3", "service_instances", "GET_response.json" diff --git a/test/v3/test_service_plans.py b/test/v3/test_service_plans.py new file mode 100644 index 0000000..7204fb2 --- /dev/null +++ b/test/v3/test_service_plans.py @@ -0,0 +1,127 @@ +import unittest +from http import HTTPStatus + +from abstract_test_case import AbstractTestCase +from cloudfoundry_client.v3.entities import Entity + + +class TestServicePlans(unittest.TestCase, AbstractTestCase): + @classmethod + def setUpClass(cls): + cls.mock_client_class() + + def setUp(self): + self.build_client() + + def test_get(self): + self.client.get.return_value = self.mock_response( + "/v3/service_plans/service_plan_guid_123", HTTPStatus.OK, None, "v3", "service_plans", "GET_{id}_response.json" + ) + service_plan = self.client.v3.service_plans.get("service_plan_guid_123") + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual("my_service_plan", service_plan["name"]) + self.assertIsInstance(service_plan, Entity) + + def test_list(self): + self.client.get.return_value = self.mock_response( + "/v3/service_plans", HTTPStatus.OK, None, "v3", "service_plans", "GET_response.json" + ) + all_service_plans = [service_plan for service_plan in self.client.v3.service_plans.list()] + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual(2, len(all_service_plans)) + self.assertEqual(all_service_plans[0]["name"], "my_big_service_plan") + self.assertIsInstance(all_service_plans[0], Entity) + + def test_update_service_plan(self): + self.client.patch.return_value = self.mock_response( + "/v3/service_plans/service_plan_guid_123", HTTPStatus.OK, None, "v3", "service_plans", "PATCH_{id}_response.json" + ) + service_plan = self.client.v3.service_plans.update( + guid="service_plan_guid_123", meta_labels={"hello": "world"}, meta_annotations={"note": "detailed information"} + ) + self.client.patch.assert_called_with( + self.client.patch.return_value.url, + json={"metadata": {"labels": {"hello": "world"}, "annotations": {"note": "detailed information"}}}, + ) + self.assertIsNotNone(service_plan) + self.assertIsInstance(service_plan, Entity) + + def test_remove(self): + self.client.delete.return_value = self.mock_response("/v3/service_plans/service_plan_id", HTTPStatus.NO_CONTENT, None) + self.client.v3.service_plans.remove("service_plan_id") + self.client.delete.assert_called_with(self.client.delete.return_value.url) + + def test_get_service_plan_visibility(self): + self.client.get.return_value = self.mock_response( + "/v3/service_plans/service_plan_guid_123/visibility", + HTTPStatus.NO_CONTENT, + None, + "v3", + "service_plans", + "GET_{id}_visibility_response.json", + ) + service_plan_visibility = self.client.v3.service_plans.get_visibility(service_plan_guid="service_plan_guid_123") + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual("public", service_plan_visibility["type"]) + + def test_update_service_plan_visibility_type(self): + self.client.patch.return_value = self.mock_response( + "/v3/service_plans/service_plan_guid_123/visibility", + HTTPStatus.NO_CONTENT, + None, + "v3", + "service_plans", + "PATCH_{id}_visibility_type_response.json", + ) + service_plan_visibility = self.client.v3.service_plans.update_visibility( + service_plan_guid="service_plan_guid_123", type="admin" + ) + self.client.patch.assert_called_with(self.client.patch.return_value.url, json={"type": "admin"}) + self.assertEqual("admin", service_plan_visibility["type"]) + + def test_update_service_plan_visibility_organizations(self): + self.client.patch.return_value = self.mock_response( + "/v3/service_plans/service_plan_guid_123/visibility", + HTTPStatus.NO_CONTENT, + None, + "v3", + "service_plans", + "PATCH_{id}_visibility_organizations_response.json", + ) + organizations = [{"guid": "0fc1ad4f-e1d7-4436-8e23-6b20f03c6482"}, {"guid": "0fc1ad4f-e1d7-4436-8e23-6b20f03c6483"}] + service_plan_visibility = self.client.v3.service_plans.update_visibility( + service_plan_guid="service_plan_guid_123", type="organization", organizations=organizations + ) + self.client.patch.assert_called_with( + self.client.patch.return_value.url, json={"type": "organization", "organizations": organizations} + ) + self.assertEqual("organization", service_plan_visibility["type"]) + self.assertEqual("some_org", service_plan_visibility["organizations"][0]["name"]) + + def test_apply_visibility_to_extra_orgs(self): + self.client.post.return_value = self.mock_response( + "/v3/service_plans/service_plan_guid_123/visibility", + HTTPStatus.NO_CONTENT, + None, + "v3", + "service_plans", + "POST_{id}_visibility_response.json", + ) + organizations = [{"guid": "b3af3658-d844-496a-8986-89b79a74c8ae"}] + service_plan_visibility = self.client.v3.service_plans.apply_visibility_to_extra_orgs( + service_plan_guid="service_plan_guid_123", organizations=organizations + ) + self.client.post.assert_called_with( + self.client.post.return_value.url, json={"type": "organization", "organizations": organizations}, files=None + ) + self.assertEqual("organization", service_plan_visibility["type"]) + self.assertEqual("b3af3658-d844-496a-8986-89b79a74c8ae", service_plan_visibility["organizations"][0]["guid"]) + + def test_remove_org_from_service_plan_visibility(self): + self.client.delete.return_value = self.mock_response( + "/v3/service_plans/service_plan_guid_123/visibility/org_guid_123", HTTPStatus.NO_CONTENT, None + ) + self.client.v3.service_plans.remove_org_from_service_plan_visibility( + service_plan_guid="service_plan_guid_123", org_guid="org_guid_123" + ) + self.client.delete.assert_called_with(self.client.delete.return_value.url) From 8c3257c21bb1327d59e1c8337d7029e5d8b60214 Mon Sep 17 00:00:00 2001 From: johha <45264872+johha@users.noreply.github.com> Date: Wed, 9 Jun 2021 09:34:43 +0200 Subject: [PATCH 084/264] Implement service offerings endpoint --- main/cloudfoundry_client/client.py | 2 + .../v3/service_offerings.py | 28 +++++ .../v3/service_offerings/GET_response.json | 109 ++++++++++++++++++ .../service_offerings/GET_{id}_response.json | 47 ++++++++ .../PATCH_{id}_response.json | 49 ++++++++ test/v3/test_service_offerings.py | 64 ++++++++++ 6 files changed, 299 insertions(+) create mode 100644 main/cloudfoundry_client/v3/service_offerings.py create mode 100644 test/fixtures/v3/service_offerings/GET_response.json create mode 100644 test/fixtures/v3/service_offerings/GET_{id}_response.json create mode 100644 test/fixtures/v3/service_offerings/PATCH_{id}_response.json create mode 100644 test/v3/test_service_offerings.py diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 30c1643..57cb4d1 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -25,6 +25,7 @@ from cloudfoundry_client.v2.service_plans import ServicePlanManagerV2 from cloudfoundry_client.v3.service_brokers import ServiceBrokerManager +from cloudfoundry_client.v3.service_offerings import ServiceOfferingsManager from cloudfoundry_client.v3.service_plans import ServicePlanManager from cloudfoundry_client.v3.apps import AppManager from cloudfoundry_client.v3.buildpacks import BuildpackManager @@ -104,6 +105,7 @@ def __init__(self, target_endpoint: str, credential_manager: "CloudFoundryClient self.jobs = JobManager(target_endpoint, credential_manager) self.service_brokers = ServiceBrokerManager(target_endpoint, credential_manager) self.service_plans = ServicePlanManager(target_endpoint, credential_manager) + self.service_offerings = ServiceOfferingsManager(target_endpoint, credential_manager) class CloudFoundryClient(CredentialManager): diff --git a/main/cloudfoundry_client/v3/service_offerings.py b/main/cloudfoundry_client/v3/service_offerings.py new file mode 100644 index 0000000..37f7d0c --- /dev/null +++ b/main/cloudfoundry_client/v3/service_offerings.py @@ -0,0 +1,28 @@ +from typing import TYPE_CHECKING, Optional + +from cloudfoundry_client.v3.entities import EntityManager, Entity + +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + + +class ServiceOfferingsManager(EntityManager): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(ServiceOfferingsManager, self).__init__(target_endpoint, client, "/v3/service_offerings") + + def update(self, guid: str, meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Entity: + payload = dict() + if meta_labels or meta_annotations: + metadata = dict() + if meta_labels: + metadata["labels"] = meta_labels + if meta_annotations: + metadata["annotations"] = meta_annotations + payload["metadata"] = metadata + return super(ServiceOfferingsManager, self)._update(guid, payload) + + def remove(self, guid: str, purge: bool = False) -> None: + url = f"{self.target_endpoint}{self.entity_uri}/{guid}" + if purge: + url += "?purge=true" + super(ServiceOfferingsManager, self)._delete(url) diff --git a/test/fixtures/v3/service_offerings/GET_response.json b/test/fixtures/v3/service_offerings/GET_response.json new file mode 100644 index 0000000..24420e8 --- /dev/null +++ b/test/fixtures/v3/service_offerings/GET_response.json @@ -0,0 +1,109 @@ +{ + "pagination": { + "total_results": 2, + "total_pages": 1, + "first": { + "href": "https://api.example.org/v3/service_offerings?page=1&per_page=50" + }, + "last": { + "href": "https://api.example.org/v3/service_offerings?page=1&per_page=50" + }, + "previous": null + }, + "resources": [ + { + "guid": "bf7eb420-11e5-11ea-b7db-4b5d5e7976a9", + "name": "my_service_offering", + "description": "Provides my service", + "available": true, + "tags": ["relational", "caching"], + "requires": [], + "created_at": "2019-11-28T13:44:02Z", + "updated_at": "2019-11-28T13:44:02Z", + "shareable": true, + "documentation_url": "https://some-documentation-link.io", + "broker_catalog": { + "id": "db730a8c-11e5-11ea-838a-0f4fff3b1cfb", + "metadata": { + "shareable": true + }, + "features": { + "plan_updateable": true, + "bindable": true, + "instances_retrievable": true, + "bindings_retrievable": true, + "allow_context_updates": false + } + }, + "relationships": { + "service_broker": { + "data": { + "guid": "13c60e38-11e7-11ea-9106-33ee3c5bd4d7" + } + } + }, + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/service_offerings/bf7eb420-11e5-11ea-b7db-4b5d5e7976a" + }, + "service_plans": { + "href": "https://api.example.org/v3/service_plans?service_offering_guids=bf7eb420-11e5-11ea-b7db-4b5d5e7976a" + }, + "service_broker": { + "href": "https://api.example.org/v3/service_brokers/13c60e38-11e7-11ea-9106-33ee3c5bd4d7" + } + } + }, + { + "guid": "20e6cd62-12bb-11ea-90d1-7bfec2c75bcd", + "name": "other_service_offering", + "description": "Provides another service", + "available": true, + "tags": ["caching"], + "requires": [], + "created_at": "2019-11-29T16:44:02Z", + "updated_at": "2019-11-29T16:44:02Z", + "shareable": true, + "documentation_url": "https://some-other-documentation-link.io", + "broker_catalog": { + "id": "3cb11822-12bb-11ea-beb1-a350dc7453b9", + "metadata": { + "shareable": true + }, + "features": { + "plan_updateable": true, + "bindable": true, + "instances_retrievable": true, + "bindings_retrievable": true, + "allow_context_updates": false + } + }, + "relationships": { + "service_broker": { + "data": { + "guid": "13c60e38-11e7-11ea-9106-33ee3c5bd4d7" + } + } + }, + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/service_offerings/20e6cd62-12bb-11ea-90d1-7bfec2c75bcd" + }, + "service_plans": { + "href": "https://api.example.org/v3/service_plans?service_offering_guids=20e6cd62-12bb-11ea-90d1-7bfec2c75bcd" + }, + "service_broker": { + "href": "https://api.example.org/v3/service_brokers/13c60e38-11e7-11ea-9106-33ee3c5bd4d7" + } + } + } + ] +} \ No newline at end of file diff --git a/test/fixtures/v3/service_offerings/GET_{id}_response.json b/test/fixtures/v3/service_offerings/GET_{id}_response.json new file mode 100644 index 0000000..41c9901 --- /dev/null +++ b/test/fixtures/v3/service_offerings/GET_{id}_response.json @@ -0,0 +1,47 @@ +{ + "guid": "service_offering_guid", + "name": "my_service_offering", + "description": "Provides my service", + "available": true, + "tags": ["relational", "caching"], + "requires": [], + "created_at": "2019-11-28T13:44:02Z", + "updated_at": "2019-11-28T13:44:02Z", + "shareable": true, + "documentation_url": "https://some-documentation-link.io", + "broker_catalog": { + "id": "db730a8c-11e5-11ea-838a-0f4fff3b1cfb", + "metadata": { + "shareable": true + }, + "features": { + "plan_updateable": true, + "bindable": true, + "instances_retrievable": true, + "bindings_retrievable": true, + "allow_context_updates": false + } + }, + "relationships": { + "service_broker": { + "data": { + "guid": "13c60e38-11e7-11ea-9106-33ee3c5bd4d7" + } + } + }, + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/service_offerings/bf7eb420-11e5-11ea-b7db-4b5d5e7976a" + }, + "service_plans": { + "href": "https://api.example.org/v3/service_plans?service_offering_guids=bf7eb420-11e5-11ea-b7db-4b5d5e7976a" + }, + "service_broker": { + "href": "https://api.example.org/v3/service_brokers/13c60e38-11e7-11ea-9106-33ee3c5bd4d7" + } + } +} diff --git a/test/fixtures/v3/service_offerings/PATCH_{id}_response.json b/test/fixtures/v3/service_offerings/PATCH_{id}_response.json new file mode 100644 index 0000000..1027c41 --- /dev/null +++ b/test/fixtures/v3/service_offerings/PATCH_{id}_response.json @@ -0,0 +1,49 @@ +{ + "guid": "service_offering_guid", + "name": "my_service_offering", + "description": "Provides my service", + "available": true, + "tags": ["relational", "caching"], + "requires": [], + "created_at": "2019-11-28T13:44:02Z", + "updated_at": "2019-11-28T13:44:02Z", + "shareable": true, + "documentation_url": "https://some-documentation-link.io", + "broker_catalog": { + "id": "db730a8c-11e5-11ea-838a-0f4fff3b1cfb", + "metadata": { + "shareable": true + }, + "features": { + "plan_updateable": true, + "bindable": true, + "instances_retrievable": true, + "bindings_retrievable": true, + "allow_context_updates": false + } + }, + "relationships": { + "service_broker": { + "data": { + "guid": "13c60e38-11e7-11ea-9106-33ee3c5bd4d7" + } + } + }, + "metadata": { + "labels": { + "hello": "world" + }, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/service_offerings/bf7eb420-11e5-11ea-b7db-4b5d5e7976a" + }, + "service_plans": { + "href": "https://api.example.org/v3/service_plans?service_offering_guids=bf7eb420-11e5-11ea-b7db-4b5d5e7976a" + }, + "service_broker": { + "href": "https://api.example.org/v3/service_brokers/13c60e38-11e7-11ea-9106-33ee3c5bd4d7" + } + } +} diff --git a/test/v3/test_service_offerings.py b/test/v3/test_service_offerings.py new file mode 100644 index 0000000..a763651 --- /dev/null +++ b/test/v3/test_service_offerings.py @@ -0,0 +1,64 @@ +import unittest +from http import HTTPStatus + +from abstract_test_case import AbstractTestCase +from cloudfoundry_client.v3.entities import Entity + + +class TestServiceOfferings(unittest.TestCase, AbstractTestCase): + @classmethod + def setUpClass(cls): + cls.mock_client_class() + + def setUp(self): + self.build_client() + + def test_get(self): + self.client.get.return_value = self.mock_response( + "/v3/service_offerings/service_offering_guid", + HTTPStatus.OK, + None, + "v3", + "service_offerings", + "GET_{id}_response.json", + ) + service_offering = self.client.v3.service_offerings.get("service_offering_guid") + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual("my_service_offering", service_offering["name"]) + self.assertIsInstance(service_offering, Entity) + + def test_list(self): + self.client.get.return_value = self.mock_response( + "/v3/service_offerings", HTTPStatus.OK, None, "v3", "service_offerings", "GET_response.json" + ) + all_service_offerings = [service_offerings for service_offerings in self.client.v3.service_offerings.list()] + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual(2, len(all_service_offerings)) + self.assertEqual(all_service_offerings[0]["name"], "my_service_offering") + self.assertIsInstance(all_service_offerings[0], Entity) + + def test_update(self): + self.client.patch.return_value = self.mock_response( + "/v3/service_offerings/service_offering_guid", + HTTPStatus.OK, + None, + "v3", + "service_offerings", + "PATCH_{id}_response.json", + ) + service_offering = self.client.v3.service_offerings.update(guid="service_offering_guid", meta_labels={"hello": "world"}) + self.client.patch.assert_called_with( + self.client.patch.return_value.url, + json={"metadata": {"labels": {"hello": "world"}}}, + ) + self.assertIsNotNone(service_offering) + self.assertIsInstance(service_offering, Entity) + + def test_remove(self): + self.client.delete.return_value = self.mock_response( + "/v3/service_offerings/service_offering_guid", HTTPStatus.NO_CONTENT, None + ) + self.client.v3.service_offerings.remove(guid="service_offering_guid", purge=True) + self.client.delete.assert_called_with( + f"{self.client.delete.return_value.url}?purge=true", + ) From 3a5ee3c52a2ae6bf4eb2a1b732194156bef66c69 Mon Sep 17 00:00:00 2001 From: Will Gant Date: Fri, 11 Jun 2021 09:25:20 +0100 Subject: [PATCH 085/264] Retain original v2 manager names, refer to them with aliases in client --- main/cloudfoundry_client/client.py | 8 ++++---- main/cloudfoundry_client/v2/jobs.py | 2 +- main/cloudfoundry_client/v2/service_brokers.py | 10 +++++----- main/cloudfoundry_client/v2/service_instances.py | 12 ++++++------ main/cloudfoundry_client/v2/service_plans.py | 4 ++-- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 57cb4d1..87e7969 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -14,15 +14,15 @@ from cloudfoundry_client.v2.buildpacks import BuildpackManager as BuildpackManagerV2 from cloudfoundry_client.v2.entities import EntityManager as EntityManagerV2 from cloudfoundry_client.v2.events import EventManager -from cloudfoundry_client.v2.jobs import JobManagerV2 +from cloudfoundry_client.v2.jobs import JobManager as JobManagerV2 from cloudfoundry_client.v2.resources import ResourceManager from cloudfoundry_client.v2.routes import RouteManager from cloudfoundry_client.v2.service_bindings import ServiceBindingManager -from cloudfoundry_client.v2.service_brokers import ServiceBrokerManagerV2 -from cloudfoundry_client.v2.service_instances import ServiceInstanceManagerV2 +from cloudfoundry_client.v2.service_brokers import ServiceBrokerManager as ServiceBrokerManagerV2 +from cloudfoundry_client.v2.service_instances import ServiceInstanceManager as ServiceInstanceManagerV2 from cloudfoundry_client.v2.service_keys import ServiceKeyManager from cloudfoundry_client.v2.service_plan_visibilities import ServicePlanVisibilityManager -from cloudfoundry_client.v2.service_plans import ServicePlanManagerV2 +from cloudfoundry_client.v2.service_plans import ServicePlanManager as ServicePlanManagerV2 from cloudfoundry_client.v3.service_brokers import ServiceBrokerManager from cloudfoundry_client.v3.service_offerings import ServiceOfferingsManager diff --git a/main/cloudfoundry_client/v2/jobs.py b/main/cloudfoundry_client/v2/jobs.py index 49c004d..0d6f30f 100644 --- a/main/cloudfoundry_client/v2/jobs.py +++ b/main/cloudfoundry_client/v2/jobs.py @@ -6,7 +6,7 @@ from cloudfoundry_client.client import CloudFoundryClient -class JobManagerV2(object): +class JobManager(object): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): self.target_endpoint = target_endpoint self.client = client diff --git a/main/cloudfoundry_client/v2/service_brokers.py b/main/cloudfoundry_client/v2/service_brokers.py index 9ebe46d..abe44c7 100644 --- a/main/cloudfoundry_client/v2/service_brokers.py +++ b/main/cloudfoundry_client/v2/service_brokers.py @@ -6,16 +6,16 @@ from cloudfoundry_client.client import CloudFoundryClient -class ServiceBrokerManagerV2(EntityManager): +class ServiceBrokerManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceBrokerManagerV2, self).__init__(target_endpoint, client, "/v2/service_brokers") + super(ServiceBrokerManager, self).__init__(target_endpoint, client, "/v2/service_brokers") def create( self, broker_url: str, broker_name: str, auth_username: str, auth_password: str, space_guid: Optional[str] = None ) -> Entity: request = self._request(broker_url=broker_url, name=broker_name, auth_username=auth_username, auth_password=auth_password) request["space_guid"] = space_guid - return super(ServiceBrokerManagerV2, self)._create(request) + return super(ServiceBrokerManager, self)._create(request) def update( self, @@ -30,7 +30,7 @@ def update( request["name"] = broker_name request["auth_username"] = auth_username request["auth_password"] = auth_password - return super(ServiceBrokerManagerV2, self)._update(broker_guid, request) + return super(ServiceBrokerManager, self)._update(broker_guid, request) def remove(self, broker_guid): - super(ServiceBrokerManagerV2, self)._remove(broker_guid) + super(ServiceBrokerManager, self)._remove(broker_guid) diff --git a/main/cloudfoundry_client/v2/service_instances.py b/main/cloudfoundry_client/v2/service_instances.py index 08674dc..f17fa7f 100644 --- a/main/cloudfoundry_client/v2/service_instances.py +++ b/main/cloudfoundry_client/v2/service_instances.py @@ -6,11 +6,11 @@ from cloudfoundry_client.client import CloudFoundryClient -class ServiceInstanceManagerV2(EntityManager): +class ServiceInstanceManager(EntityManager): list_query_parameters = ["page", "results-per-page", "order-direction", "return_user_provided_service_instances"] def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceInstanceManagerV2, self).__init__(target_endpoint, client, "/v2/service_instances") + super(ServiceInstanceManager, self).__init__(target_endpoint, client, "/v2/service_instances") def create( self, @@ -25,7 +25,7 @@ def create( request["parameters"] = parameters request["tags"] = tags params = None if not accepts_incomplete else dict(accepts_incomplete="true") - return super(ServiceInstanceManagerV2, self)._create(request, params=params) + return super(ServiceInstanceManager, self)._create(request, params=params) def update( self, @@ -42,10 +42,10 @@ def update( request["parameters"] = parameters request["tags"] = tags params = None if not accepts_incomplete else dict(accepts_incomplete="true") - return super(ServiceInstanceManagerV2, self)._update(instance_guid, request, params=params) + return super(ServiceInstanceManager, self)._update(instance_guid, request, params=params) def list_permissions(self, instance_guid: str) -> Dict[str, bool]: - return super(ServiceInstanceManagerV2, self)._get("%s/%s/permissions" % (self.entity_uri, instance_guid), dict) + return super(ServiceInstanceManager, self)._get("%s/%s/permissions" % (self.entity_uri, instance_guid), dict) def remove(self, instance_guid: str, accepts_incomplete: Optional[bool] = False, purge: Optional[bool] = False): parameters = {} @@ -53,4 +53,4 @@ def remove(self, instance_guid: str, accepts_incomplete: Optional[bool] = False, parameters["accepts_incomplete"] = "true" if purge: parameters["purge"] = "true" - super(ServiceInstanceManagerV2, self)._remove(instance_guid, params=parameters) + super(ServiceInstanceManager, self)._remove(instance_guid, params=parameters) diff --git a/main/cloudfoundry_client/v2/service_plans.py b/main/cloudfoundry_client/v2/service_plans.py index 5d79619..487b1cd 100644 --- a/main/cloudfoundry_client/v2/service_plans.py +++ b/main/cloudfoundry_client/v2/service_plans.py @@ -6,9 +6,9 @@ from cloudfoundry_client.client import CloudFoundryClient -class ServicePlanManagerV2(EntityManager): +class ServicePlanManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServicePlanManagerV2, self).__init__(target_endpoint, client, "/v2/service_plans") + super(ServicePlanManager, self).__init__(target_endpoint, client, "/v2/service_plans") def create_from_resource_file(self, path: str) -> Entity: raise NotImplementedError("No creation allowed") From e3719cdb2159e7e1e2254e67040cc984d0d07773 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Mon, 14 Jun 2021 11:18:06 +0200 Subject: [PATCH 086/264] Prepare version 1.19.0 --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 0a2928f..4e3bc5f 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.18.0" +__version__ = "1.19.0" From 36b79bb090c705add481e710d85c452d7fdaa6b0 Mon Sep 17 00:00:00 2001 From: Philipp Thun Date: Mon, 31 May 2021 14:20:52 +0200 Subject: [PATCH 087/264] Refactor Entity constructor The mapping between link methods (e.g. "GET") and callable methods of the corresponding entity manager (e.g. "_paginate") is done in static method "_manager_method". App overwrites this method to ensure that "_get" is invoked instead of "_paginate" for the environment variables link. --- main/cloudfoundry_client/v3/apps.py | 17 +++---- main/cloudfoundry_client/v3/entities.py | 61 ++++++++++++++++--------- 2 files changed, 45 insertions(+), 33 deletions(-) diff --git a/main/cloudfoundry_client/v3/apps.py b/main/cloudfoundry_client/v3/apps.py index cd9ed0e..29c3160 100644 --- a/main/cloudfoundry_client/v3/apps.py +++ b/main/cloudfoundry_client/v3/apps.py @@ -1,5 +1,4 @@ -import functools -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional from cloudfoundry_client.json_object import JsonObject from cloudfoundry_client.v3.entities import EntityManager, Entity @@ -9,15 +8,11 @@ class App(Entity): - def __init__(self, target_endpoint: str, client: "CloudFoundryClient", **kwargs): - super(App, self).__init__(target_endpoint, client, **kwargs) - # patch environment_variables method - environment_variables_link = self.get("links", {}).get("environment_variables", {}).get("href", None) - if environment_variables_link is not None: - other_manager = self._default_manager(client, target_endpoint) - new_method = functools.partial(other_manager._get, environment_variables_link) - new_method.__name__ = "environment_variables" - setattr(self, "environment_variables", new_method) + @staticmethod + def _manager_method(link_name: str, link_method: str) -> Optional[str]: + if link_name == "environment_variables" and link_method == "get": + return "_get" # instead of _paginate + return Entity._manager_method(link_name, link_method) class AppManager(EntityManager): diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index 3565e01..53f99c1 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -1,6 +1,6 @@ import functools from json import JSONDecodeError -from typing import Any, Generator, Optional, List, Tuple, Union, TypeVar, TYPE_CHECKING +from typing import Any, Generator, Optional, List, Tuple, Union, TypeVar, TYPE_CHECKING, Callable from urllib.parse import quote, urlparse from requests import Response @@ -10,36 +10,31 @@ from cloudfoundry_client.request_object import Request if TYPE_CHECKING: - from cloudfoundry_client.client import CloudFoundryClient + from cloudfoundry_client.client import CloudFoundryClient, V3 + + +def plural(name: str) -> str: + return name if name.endswith("s") else "%ss" % name class Entity(JsonObject): def __init__(self, target_endpoint: str, client: "CloudFoundryClient", **kwargs): super(Entity, self).__init__(**kwargs) - try: + default_manager = self._default_manager(target_endpoint, client) + self._create_navigable_links(client.v3, default_manager) - def default_method(m, u): - raise NotImplementedError("Unknown method %s for url %s" % (m, u)) - - default_manager = self._default_manager(client, target_endpoint) + def _create_navigable_links(self, v3_client: "V3", default_manager: "EntityManager") -> None: + try: + default_method = self._default_method() for link_name, link in self.get("links", {}).items(): if link_name != "self": link_method = link.get("method", "GET").lower() + manager_method = self._manager_method(link_name, link_method) ref = link["href"] - manager_name = link_name if link_name.endswith("s") else "%ss" % link_name - other_manager = getattr(client.v3, manager_name, default_manager) - if link_method == "get": - new_method = ( - functools.partial(other_manager._paginate, ref) - if link_name.endswith("s") - else functools.partial(other_manager._get, ref) - ) - elif link_method == "post": - new_method = functools.partial(other_manager._post, ref) - elif link_method == "put": - new_method = functools.partial(other_manager._put, ref) - elif link_method == "delete": - new_method = functools.partial(other_manager._delete, ref) + if manager_method is not None: + manager_name = plural(link_name) + other_manager = getattr(v3_client, manager_name, default_manager) + new_method = functools.partial(getattr(other_manager, manager_method), ref) else: new_method = functools.partial(default_method, link_method, ref) new_method.__name__ = link_name @@ -48,9 +43,31 @@ def default_method(m, u): raise InvalidEntity(**self) @staticmethod - def _default_manager(client, target_endpoint): + def _default_manager(target_endpoint: str, client: "CloudFoundryClient") -> "EntityManager": return EntityManager(target_endpoint, client, "") + @staticmethod + def _default_method() -> Callable: + def default_method(m, u): + raise NotImplementedError("Unknown method %s for url %s" % (m, u)) + + return default_method + + @staticmethod + def _manager_method(link_name: str, link_method: str) -> Optional[str]: + if link_method == "get": + if link_name.endswith("s"): + return "_paginate" + else: + return "_get" + elif link_method == "post": + return "_post" + elif link_method == "put": + return "_put" + elif link_method == "delete": + return "_delete" + return None + PaginateEntities = Generator[Entity, None, None] From 81bd7b824742624aec15013b1ac9d3afc3447544 Mon Sep 17 00:00:00 2001 From: Philipp Thun Date: Mon, 31 May 2021 15:51:57 +0200 Subject: [PATCH 088/264] Return included entities Some api endpoints [1] support the inclusion of parent entities. From the currently supported V3 endpoints these are: Apps and Spaces Added "kwargs" parameter to EntityManager.get; both "get" and "list" can be called with an "include" parameter. EntityManager._get_url_filtered is now "_get_url_with_encoded_params" and is reused for encoding arbitrary "get" parameters. EntityManager._read_response calls "_mixin_included_entities" to include the parent entities in the corresponding resource (they are returned in a separate "included" section by the api). For included entities the Entity constructor overwrites the link method and simply returns the included entity. This is illustrated in the following examples: Example 1 --------- The client issues two requests to the api: - GET /v3/apps/app_id and - GET /v3/spaces/space_id app = client.v3.apps.get("app_id") space = app.space() Example 2 --------- The client issues a single request to the api: - GET /v3/apps/app_id?include=space app = client.v3.apps.get("app_id", include="space") space = app.space() EntityManager._get_entity_type can be overwritten to return a specific subclass of Entity based on the included name. [1] https://v3-apidocs.cloudfoundry.org/version/3.100.0/index.html#resources-with-includes --- README.rst | 22 ++- main/cloudfoundry_client/v3/entities.py | 52 +++++- .../v3/apps/GET_response_include_space.json | 170 ++++++++++++++++++ ...T_{id}_response_include_space_and_org.json | 107 +++++++++++ .../v3/spaces/GET_response_include_org.json | 96 ++++++++++ .../spaces/GET_{id}_response_include_org.json | 42 +++++ test/v3/test_apps.py | 32 +++- test/v3/test_spaces.py | 34 +++- 8 files changed, 543 insertions(+), 12 deletions(-) create mode 100644 test/fixtures/v3/apps/GET_response_include_space.json create mode 100644 test/fixtures/v3/apps/GET_{id}_response_include_space_and_org.json create mode 100644 test/fixtures/v3/spaces/GET_response_include_org.json create mode 100644 test/fixtures/v3/spaces/GET_{id}_response_include_org.json diff --git a/README.rst b/README.rst index 457b717..bbbab8a 100644 --- a/README.rst +++ b/README.rst @@ -215,6 +215,23 @@ Another example: for task in client.v3.tasks.list(app_guids=['app-guid-1', 'app-guid-2']): task.cancel() +When supported by the API, parent entities can be included in a single call. The included entities replace the links mentioned above. +The following code snippet issues three requests to the API in order to get app, space and organization data: + +.. code-block:: python + + app = client.v3.apps.get("app-guid") + print("App name: %s" % app["name]) + space = app.space() + print("Space name: %s" % space["name]) + org = space.organization() + print("Org name: %s" % org["name]) + +By changing the first line only, a single request fetches all the data. The navigation from app to space and space to organization remains unchanged. + +.. code-block:: python + + app = client.v3.apps.get("app-guid", include="space.organization") Available managers on API V3 are: @@ -227,7 +244,10 @@ Available managers on API V3 are: - ``spaces`` - ``tasks`` -The managers provide the same methods as the V2 managers. +The managers provide the same methods as the V2 managers with the following differences: + +- ``get(**kwargs)``: supports keyword arguments that are passed on to the API, e.g. "include" + Networking ---------- diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index 53f99c1..1b50194 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -1,6 +1,6 @@ import functools from json import JSONDecodeError -from typing import Any, Generator, Optional, List, Tuple, Union, TypeVar, TYPE_CHECKING, Callable +from typing import Any, Generator, Optional, List, Tuple, Union, TypeVar, TYPE_CHECKING, Callable, Type from urllib.parse import quote, urlparse from requests import Response @@ -22,6 +22,7 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient", **kwargs) super(Entity, self).__init__(**kwargs) default_manager = self._default_manager(target_endpoint, client) self._create_navigable_links(client.v3, default_manager) + self._create_navigable_included_entities(client.v3, default_manager) def _create_navigable_links(self, v3_client: "V3", default_manager: "EntityManager") -> None: try: @@ -42,6 +43,15 @@ def _create_navigable_links(self, v3_client: "V3", default_manager: "EntityManag except KeyError: raise InvalidEntity(**self) + def _create_navigable_included_entities(self, v3_client: "V3", default_manager: "EntityManager") -> None: + for entity_name, entity_data in self.get("_included", {}).items(): + manager_name = plural(entity_name) + other_manager = getattr(v3_client, manager_name, default_manager) + entity_type = other_manager._get_entity_type(entity_name) + entity = entity_type(other_manager.target_endpoint, other_manager.client, **entity_data) + setattr(self, entity_name, lambda: entity) + self.pop("_included", None) + @staticmethod def _default_manager(target_endpoint: str, client: "CloudFoundryClient") -> "EntityManager": return EntityManager(target_endpoint, client, "") @@ -118,8 +128,9 @@ def _post(self, url: str, data: Optional[dict] = None, files: Any = None, entity response = self.client.post(url, json=data, files=files) return self._read_response(response, entity_type) - def _get(self, url: str, entity_type: Optional[ENTITY_TYPE] = None) -> Entity: - response = self.client.get(url) + def _get(self, url: str, entity_type: Optional[ENTITY_TYPE] = None, **kwargs) -> Entity: + url_requested = EntityManager._get_url_with_encoded_params(url, **kwargs) + response = self.client.get(url_requested) return self._read_response(response, entity_type) def _put(self, url: str, data: dict, entity_type: Optional[ENTITY_TYPE] = None) -> Entity: @@ -138,7 +149,8 @@ def _delete(self, url: str) -> Optional[str]: return None def _list(self, requested_path: str, entity_type: Optional[ENTITY_TYPE] = None, **kwargs) -> PaginateEntities: - url_requested = EntityManager._get_url_filtered("%s%s" % (self.target_endpoint, requested_path), **kwargs) + url_requested = EntityManager._get_url_with_encoded_params("%s%s" % (self.target_endpoint, requested_path), + **kwargs) for element in self._paginate(url_requested, entity_type): yield element @@ -194,12 +206,12 @@ def get_first(self, **kwargs) -> Optional[Entity]: return entity return None - def get(self, entity_id: str, *extra_paths) -> Entity: + def get(self, entity_id: str, *extra_paths, **kwargs) -> Entity: if len(extra_paths) == 0: requested_path = "%s%s/%s" % (self.target_endpoint, self.entity_uri, entity_id) else: requested_path = "%s%s/%s/%s" % (self.target_endpoint, self.entity_uri, entity_id, "/".join(extra_paths)) - return self._get(requested_path) + return self._get(requested_path, **kwargs) def _read_response(self, response: Response, entity_type: Optional[ENTITY_TYPE]) -> Union[JsonObject, Entity]: try: @@ -214,12 +226,36 @@ def _read_response(self, response: Response, entity_type: Optional[ENTITY_TYPE]) "method": "GET", } - return self._entity(result, entity_type) + return self._entity(self._mixin_included_resources(result), entity_type) @staticmethod def _request(**mandatory_parameters) -> Request: return Request(**mandatory_parameters) + def _mixin_included_resources(self, result: JsonObject) -> JsonObject: + if "included" not in result: + return result + for resource in result.get("resources", [result]): + self._include_resources(resource, result) + del result["included"] + return result + + def _include_resources(self, resource: JsonObject, result: JsonObject) -> None: + for relationship_name, relationship in resource.get("relationships", {}).items(): + relationship_guid = relationship.get("data", {}).get("guid", None) + included_resources = result["included"].get(plural(relationship_name), None) + if relationship_guid is not None and included_resources is not None: + included_resource = next((r for r in included_resources if relationship_guid == r.get("guid", None)), + None) + if included_resource is not None: + self._include_resources(included_resource, result) + included = resource.setdefault("_included", {}) + included.update({relationship_name: included_resource}) + + @staticmethod + def _get_entity_type(entity_name: str) -> Type[ENTITY_TYPE]: + return Entity + def _entity(self, result: JsonObject, entity_type: Optional[ENTITY_TYPE]) -> Union[JsonObject, Entity]: if "guid" in result or ("links" in result and "job" in result["links"]): return (entity_type or self.entity_type)(self.target_endpoint, self.client, **result) @@ -227,7 +263,7 @@ def _entity(self, result: JsonObject, entity_type: Optional[ENTITY_TYPE]) -> Uni return result @staticmethod - def _get_url_filtered(url: str, **kwargs) -> str: + def _get_url_with_encoded_params(url: str, **kwargs) -> str: def _append_encoded_parameter(parameters: List[str], args: Tuple[str, Any]) -> List[str]: parameter_name, parameter_value = args[0], args[1] if isinstance(parameter_value, (list, tuple)): diff --git a/test/fixtures/v3/apps/GET_response_include_space.json b/test/fixtures/v3/apps/GET_response_include_space.json new file mode 100644 index 0000000..5a6c91b --- /dev/null +++ b/test/fixtures/v3/apps/GET_response_include_space.json @@ -0,0 +1,170 @@ +{ + "pagination": { + "total_results": 3, + "total_pages": 2, + "first": { + "href": "http://somewhere.org/v3/apps?page=1&per_page=2" + }, + "last": { + "href": "http://somewhere.org/v3/apps?page=2&per_page=2" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "1cb006ee-fb05-47e1-b541-c34179ddc446", + "name": "my_app", + "state": "STARTED", + "created_at": "2016-03-17T21:41:30Z", + "updated_at": "2016-03-18T11:32:30Z", + "lifecycle": { + "type": "buildpack", + "data": { + "buildpacks": [ + "java_buildpack" + ], + "stack": "cflinuxfs2" + } + }, + "relationships": { + "space": { + "data": { + "guid": "2f35885d-0c9d-4423-83ad-fd05066f8576" + } + } + }, + "links": { + "self": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446" + }, + "space": { + "href": "http://somewhere.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576" + }, + "processes": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/processes" + }, + "route_mappings": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/route_mappings" + }, + "packages": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/packages" + }, + "environment_variables": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/environment_variables" + }, + "current_droplet": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/droplets/current" + }, + "droplets": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/droplets" + }, + "tasks": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/tasks" + }, + "start": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/actions/start", + "method": "POST" + }, + "stop": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/actions/stop", + "method": "POST" + } + }, + "metadata": { + "labels": {} + } + }, + { + "guid": "02b4ec9b-94c7-4468-9c23-4e906191a0f8", + "name": "my_app2", + "state": "STOPPED", + "created_at": "1970-01-01T00:00:02Z", + "updated_at": "2016-06-08T16:41:26Z", + "lifecycle": { + "type": "buildpack", + "data": { + "buildpacks": [ + "ruby_buildpack" + ], + "stack": "cflinuxfs2" + } + }, + "relationships": { + "space": { + "data": { + "guid": "2f35885d-0c9d-4423-83ad-fd05066f8576" + } + } + }, + "links": { + "self": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8" + }, + "space": { + "href": "http://somewhere.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576" + }, + "processes": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/processes" + }, + "route_mappings": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/route_mappings" + }, + "packages": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/packages" + }, + "environment_variables": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/environment_variables" + }, + "current_droplet": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/droplets/current" + }, + "droplets": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/droplets" + }, + "tasks": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/tasks" + }, + "start": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/actions/start", + "method": "POST" + }, + "stop": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/actions/stop", + "method": "POST" + } + }, + "metadata": { + "labels": {} + } + } + ], + "included": { + "spaces": [ + { + "guid": "2f35885d-0c9d-4423-83ad-fd05066f8576", + "created_at": "2017-02-01T01:33:58Z", + "updated_at": "2017-02-01T01:33:58Z", + "name": "my_space", + "relationships": { + "organization": { + "data": { + "guid": "e00705b9-7b42-4561-ae97-2520399d2133" + } + } + }, + "links": { + "self": { + "href": "http://somewhere.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576" + }, + "organization": { + "href": "http://somewhere.org/v3/organizations/e00705b9-7b42-4561-ae97-2520399d2133" + } + }, + "metadata": { + "labels": {} + } + } + ] + } +} \ No newline at end of file diff --git a/test/fixtures/v3/apps/GET_{id}_response_include_space_and_org.json b/test/fixtures/v3/apps/GET_{id}_response_include_space_and_org.json new file mode 100644 index 0000000..5255f52 --- /dev/null +++ b/test/fixtures/v3/apps/GET_{id}_response_include_space_and_org.json @@ -0,0 +1,107 @@ +{ + "guid": "app_id", + "name": "my_app", + "state": "STOPPED", + "created_at": "2016-03-17T21:41:30Z", + "updated_at": "2016-06-08T16:41:26Z", + "lifecycle": { + "type": "buildpack", + "data": { + "buildpacks": [ + "java_buildpack" + ], + "stack": "cflinuxfs2" + } + }, + "relationships": { + "space": { + "data": { + "guid": "2f35885d-0c9d-4423-83ad-fd05066f8576" + } + } + }, + "links": { + "self": { + "href": "http://somewhere.org/v3/apps/app_id" + }, + "space": { + "href": "http://somewhere.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576" + }, + "processes": { + "href": "http://somewhere.org/v3/apps/app_id/processes" + }, + "route_mappings": { + "href": "http://somewhere.org/v3/apps/app_id/route_mappings" + }, + "packages": { + "href": "http://somewhere.org/v3/apps/app_id/packages" + }, + "environment_variables": { + "href": "http://somewhere.org/v3/apps/app_id/environment_variables" + }, + "current_droplet": { + "href": "http://somewhere.org/v3/apps/app_id/droplets/current" + }, + "droplets": { + "href": "http://somewhere.org/v3/apps/app_id/droplets" + }, + "tasks": { + "href": "http://somewhere.org/v3/apps/app_id/tasks" + }, + "start": { + "href": "http://somewhere.org/v3/apps/app_id/actions/start", + "method": "POST" + }, + "stop": { + "href": "http://somewhere.org/v3/apps/app_id/actions/stop", + "method": "POST" + } + }, + "metadata": { + "labels": {} + }, + "included": { + "spaces": [ + { + "guid": "2f35885d-0c9d-4423-83ad-fd05066f8576", + "created_at": "2017-02-01T01:33:58Z", + "updated_at": "2017-02-01T01:33:58Z", + "name": "my_space", + "relationships": { + "organization": { + "data": { + "guid": "e00705b9-7b42-4561-ae97-2520399d2133" + } + } + }, + "links": { + "self": { + "href": "http://somewhere.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576" + }, + "organization": { + "href": "http://somewhere.org/v3/organizations/e00705b9-7b42-4561-ae97-2520399d2133" + } + }, + "metadata": { + "labels": {} + } + } + ], + "organizations": [ + { + "guid": "e00705b9-7b42-4561-ae97-2520399d2133", + "created_at": "2017-02-01T01:33:58Z", + "updated_at": "2017-02-01T01:33:58Z", + "name": "my_organization", + "links": { + "self": { + "href": "http://somewhere.org/v3/organizations/e00705b9-7b42-4561-ae97-2520399d2133" + } + }, + "metadata": { + "labels": {} + } + } + ] + } +} \ No newline at end of file diff --git a/test/fixtures/v3/spaces/GET_response_include_org.json b/test/fixtures/v3/spaces/GET_response_include_org.json new file mode 100644 index 0000000..b52493d --- /dev/null +++ b/test/fixtures/v3/spaces/GET_response_include_org.json @@ -0,0 +1,96 @@ +{ + "pagination": { + "total_results": 2, + "total_pages": 1, + "first": { + "href": "http://somewhere.org/v3/spaces?page=1&per_page=50" + }, + "last": { + "href": "http://somewhere.org/v3/spaces?page=1&per_page=50" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "885735b5-aea4-4cf5-8e44-961af0e41920", + "created_at": "2017-02-01T01:33:58Z", + "updated_at": "2017-02-01T01:33:58Z", + "name": "space1", + "relationships": { + "organization": { + "data": { + "guid": "e00705b9-7b42-4561-ae97-2520399d2133" + } + } + }, + "links": { + "self": { + "href": "http://somewhere.org/v3/spaces/885735b5-aea4-4cf5-8e44-961af0e41920" + }, + "organization": { + "href": "http://somewhere.org/v3/organizations/e00705b9-7b42-4561-ae97-2520399d2133" + } + }, + "metadata": { + "labels": {} + } + }, + { + "guid": "d4c91047-7b29-4fda-b7f9-04033e5c9c9f", + "created_at": "2017-02-02T00:14:30Z", + "updated_at": "2017-02-02T00:14:30Z", + "name": "space2", + "relationships": { + "organization": { + "data": { + "guid": "b4ce91bd-31df-4b7d-8fd4-21a6b533276b" + } + } + }, + "links": { + "self": { + "href": "http://somewhere.org/v3/spaces/d4c91047-7b29-4fda-b7f9-04033e5c9c9f" + }, + "organization": { + "href": "http://somewhere.org/v3/organizations/b4ce91bd-31df-4b7d-8fd4-21a6b533276b" + } + }, + "metadata": { + "labels": {} + } + } + ], + "included": { + "organizations": [ + { + "guid": "e00705b9-7b42-4561-ae97-2520399d2133", + "created_at": "2017-02-01T01:33:58Z", + "updated_at": "2017-02-01T01:33:58Z", + "name": "org1", + "links": { + "self": { + "href": "http://somewhere.org/v3/organizations/e00705b9-7b42-4561-ae97-2520399d2133" + } + }, + "metadata": { + "labels": {} + } + }, + { + "guid": "b4ce91bd-31df-4b7d-8fd4-21a6b533276b", + "created_at": "2017-02-02T00:14:30Z", + "updated_at": "2017-02-02T00:14:30Z", + "name": "org2", + "links": { + "self": { + "href": "http://somewhere.org/v3/organizations/b4ce91bd-31df-4b7d-8fd4-21a6b533276b" + } + }, + "metadata": { + "labels": {} + } + } + ] + } +} \ No newline at end of file diff --git a/test/fixtures/v3/spaces/GET_{id}_response_include_org.json b/test/fixtures/v3/spaces/GET_{id}_response_include_org.json new file mode 100644 index 0000000..d5c51c5 --- /dev/null +++ b/test/fixtures/v3/spaces/GET_{id}_response_include_org.json @@ -0,0 +1,42 @@ +{ + "guid": "885735b5-aea4-4cf5-8e44-961af0e41920", + "created_at": "2017-02-01T01:33:58Z", + "updated_at": "2017-02-01T01:33:58Z", + "name": "my-space", + "relationships": { + "organization": { + "data": { + "guid": "e00705b9-7b42-4561-ae97-2520399d2133" + } + } + }, + "links": { + "self": { + "href": "http://somewhere.org/v3/spaces/885735b5-aea4-4cf5-8e44-961af0e41920" + }, + "organization": { + "href": "http://somewhere.org/v3/organizations/e00705b9-7b42-4561-ae97-2520399d2133" + } + }, + "metadata": { + "labels": {} + }, + "included": { + "organizations": [ + { + "guid": "e00705b9-7b42-4561-ae97-2520399d2133", + "created_at": "2017-02-01T01:33:58Z", + "updated_at": "2017-02-01T01:33:58Z", + "name": "my-organization", + "links": { + "self": { + "href": "http://somewhere.org/v3/organizations/e00705b9-7b42-4561-ae97-2520399d2133" + } + }, + "metadata": { + "labels": {} + } + } + ] + } +} \ No newline at end of file diff --git a/test/v3/test_apps.py b/test/v3/test_apps.py index 666cd28..d018af7 100644 --- a/test/v3/test_apps.py +++ b/test/v3/test_apps.py @@ -68,7 +68,8 @@ def test_get_then_environment_variables(self): "GET_{id}_environment_variables_response.json", ) self.client.get.side_effect = [get_app, get_environment_variables] - environment_variables = self.client.v3.apps.get("app_id").environment_variables() + app = self.client.v3.apps.get("app_id") + environment_variables = app.environment_variables() self.assertIsInstance(environment_variables, dict) self.assertEqual("production", environment_variables["var"]["RAILS_ENV"]) @@ -92,3 +93,32 @@ def test_get_routes(self): routes = self.client.v3.apps.get_routes("app_id") self.assertIsInstance(routes, JsonObject) self.assertEquals(routes["resources"][0]["destinations"][0]["guid"], "385bf117-17f5-4689-8c5c-08c6cc821fed") + + def test_get_include_space_and_org(self): + self.client.get.return_value = self.mock_response( + "/v3/apps/app_id?include=space.organization", + HTTPStatus.OK, + None, + "v3", + "apps", + "GET_{id}_response_include_space_and_org.json" + ) + space = self.client.v3.apps.get("app_id", include="space.organization").space() + self.client.get.assert_called_with(self.client.get.return_value.url) + org = space.organization() + self.assertEqual("my_space", space["name"]) + self.assertIsInstance(space, Entity) + self.assertEqual("my_organization", org["name"]) + self.assertIsInstance(org, Entity) + + def test_list_include_space(self): + self.client.get.return_value = self.mock_response( + "/v3/apps?include=space", HTTPStatus.OK, None, "v3", "apps", "GET_response_include_space.json" + ) + all_spaces = [app.space() for app in self.client.v3.apps.list(include="space")] + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual(2, len(all_spaces)) + self.assertEqual(all_spaces[0]["name"], "my_space") + self.assertIsInstance(all_spaces[0], Entity) + self.assertEqual(all_spaces[1]["name"], "my_space") + self.assertIsInstance(all_spaces[1], Entity) diff --git a/test/v3/test_spaces.py b/test/v3/test_spaces.py index 6a612f7..5963200 100644 --- a/test/v3/test_spaces.py +++ b/test/v3/test_spaces.py @@ -28,7 +28,9 @@ def test_create(self): self.assertIsInstance(result, Entity) def test_list(self): - self.client.get.return_value = self.mock_response("/v3/spaces", HTTPStatus.OK, None, "v3", "spaces", "GET_response.json") + self.client.get.return_value = self.mock_response( + "/v3/spaces", HTTPStatus.OK, None, "v3", "spaces", "GET_response.json" + ) all_spaces = [space for space in self.client.v3.spaces.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(2, len(all_spaces)) @@ -45,7 +47,9 @@ def test_get(self): self.assertIsInstance(space, Entity) def test_get_then_organization(self): - get_space = self.mock_response("/v3/spaces/space_id", HTTPStatus.OK, None, "v3", "spaces", "GET_{id}_response.json") + get_space = self.mock_response( + "/v3/spaces/space_id", HTTPStatus.OK, None, "v3", "spaces", "GET_{id}_response.json" + ) get_organization = self.mock_response( "/v3/organizations/e00705b9-7b42-4561-ae97-2520399d2133", HTTPStatus.OK, @@ -92,3 +96,29 @@ def test_remove(self): self.client.delete.return_value = self.mock_response("/v3/spaces/space_id", HTTPStatus.NO_CONTENT, None) self.client.v3.spaces.remove("space_id") self.client.delete.assert_called_with(self.client.delete.return_value.url) + + def test_get_include_organization(self): + self.client.get.return_value = self.mock_response( + "/v3/spaces/space_id?include=organization", + HTTPStatus.OK, + None, + "v3", + "spaces", + "GET_{id}_response_include_org.json" + ) + org = self.client.v3.spaces.get("space_id", include="organization").organization() + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual("my-organization", org["name"]) + self.assertIsInstance(org, Entity) + + def test_list_include_organization(self): + self.client.get.return_value = self.mock_response( + "/v3/spaces?include=organization", HTTPStatus.OK, None, "v3", "spaces", "GET_response_include_org.json" + ) + all_orgs = [space.organization() for space in self.client.v3.spaces.list(include="organization")] + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual(2, len(all_orgs)) + self.assertEqual(all_orgs[0]["name"], "org1") + self.assertIsInstance(all_orgs[0], Entity) + self.assertEqual(all_orgs[1]["name"], "org2") + self.assertIsInstance(all_orgs[1], Entity) From 7d148b7414e1b0084c4dd621d6e5b53b329bbd83 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sun, 20 Jun 2021 20:02:02 +0200 Subject: [PATCH 089/264] Prepare version 1.20.0 --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 4e3bc5f..6c632a8 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.19.0" +__version__ = "1.20.0" From f09e9175ea184d50a055d01f48113caefe069456 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sun, 20 Jun 2021 20:59:01 +0200 Subject: [PATCH 090/264] Added processes v3 handler --- main/cloudfoundry_client/client.py | 18 +-- main/cloudfoundry_client/v3/processes.py | 19 +++ test/fixtures/v3/processes/GET_response.json | 112 ++++++++++++++++++ .../v3/processes/GET_{id}_response.json | 50 ++++++++ test/v3/test_processes.py | 32 +++++ 5 files changed, 223 insertions(+), 8 deletions(-) create mode 100644 main/cloudfoundry_client/v3/processes.py create mode 100644 test/fixtures/v3/processes/GET_response.json create mode 100644 test/fixtures/v3/processes/GET_{id}_response.json create mode 100644 test/v3/test_processes.py diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index b878648..0a43132 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -24,16 +24,17 @@ from cloudfoundry_client.v2.service_plan_visibilities import ServicePlanVisibilityManager from cloudfoundry_client.v2.service_plans import ServicePlanManager as ServicePlanManagerV2 -from cloudfoundry_client.v3.service_brokers import ServiceBrokerManager -from cloudfoundry_client.v3.service_offerings import ServiceOfferingsManager -from cloudfoundry_client.v3.service_plans import ServicePlanManager from cloudfoundry_client.v3.apps import AppManager from cloudfoundry_client.v3.buildpacks import BuildpackManager from cloudfoundry_client.v3.domains import DomainManager from cloudfoundry_client.v3.feature_flags import FeatureFlagManager from cloudfoundry_client.v3.isolation_segments import IsolationSegmentManager +from cloudfoundry_client.v3.processes import ProcessManager from cloudfoundry_client.v3.organizations import OrganizationManager +from cloudfoundry_client.v3.service_brokers import ServiceBrokerManager from cloudfoundry_client.v3.service_instances import ServiceInstanceManager +from cloudfoundry_client.v3.service_offerings import ServiceOfferingsManager +from cloudfoundry_client.v3.service_plans import ServicePlanManager from cloudfoundry_client.v3.spaces import SpaceManager from cloudfoundry_client.v3.tasks import TaskManager from cloudfoundry_client.v3.jobs import JobManager @@ -98,14 +99,15 @@ def __init__(self, target_endpoint: str, credential_manager: "CloudFoundryClient self.domains = DomainManager(target_endpoint, credential_manager) self.feature_flags = FeatureFlagManager(target_endpoint, credential_manager) self.isolation_segments = IsolationSegmentManager(target_endpoint, credential_manager) - self.spaces = SpaceManager(target_endpoint, credential_manager) - self.organizations = OrganizationManager(target_endpoint, credential_manager) - self.service_instances = ServiceInstanceManager(target_endpoint, credential_manager) - self.tasks = TaskManager(target_endpoint, credential_manager) self.jobs = JobManager(target_endpoint, credential_manager) + self.organizations = OrganizationManager(target_endpoint, credential_manager) + self.processes = ProcessManager(target_endpoint, credential_manager) self.service_brokers = ServiceBrokerManager(target_endpoint, credential_manager) - self.service_plans = ServicePlanManager(target_endpoint, credential_manager) + self.service_instances = ServiceInstanceManager(target_endpoint, credential_manager) self.service_offerings = ServiceOfferingsManager(target_endpoint, credential_manager) + self.service_plans = ServicePlanManager(target_endpoint, credential_manager) + self.spaces = SpaceManager(target_endpoint, credential_manager) + self.tasks = TaskManager(target_endpoint, credential_manager) class CloudFoundryClient(CredentialManager): diff --git a/main/cloudfoundry_client/v3/processes.py b/main/cloudfoundry_client/v3/processes.py new file mode 100644 index 0000000..076ab8e --- /dev/null +++ b/main/cloudfoundry_client/v3/processes.py @@ -0,0 +1,19 @@ +from typing import TYPE_CHECKING, Optional + +from cloudfoundry_client.v3.entities import EntityManager, Entity + +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + + +class Process(Entity): + @staticmethod + def _manager_method(link_name: str, link_method: str) -> Optional[str]: + if link_name == "stats" and link_method == "get": + return "_get" # instead of _paginate + return Entity._manager_method(link_name, link_method) + + +class ProcessManager(EntityManager): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(ProcessManager, self).__init__(target_endpoint, client, "/v3/processes", Process) diff --git a/test/fixtures/v3/processes/GET_response.json b/test/fixtures/v3/processes/GET_response.json new file mode 100644 index 0000000..34212a8 --- /dev/null +++ b/test/fixtures/v3/processes/GET_response.json @@ -0,0 +1,112 @@ +{ + "pagination": { + "total_results": 3, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/processes?page=1&per_page=2" + }, + "last": { + "href": "https://api.example.org/v3/processes?page=2&per_page=2" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "6a901b7c-9417-4dc1-8189-d3234aa0ab82", + "type": "web", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 5, + "memory_in_mb": 256, + "disk_in_mb": 1024, + "health_check": { + "type": "port", + "data": { + "timeout": null + } + }, + "relationships": { + "app": { + "data": { + "guid": "ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5" + } + }, + "revision": { + "data": { + "guid": "885735b5-aea4-4cf5-8e44-961af0e41920" + } + } + }, + "metadata": { + "labels": {}, + "annotations": {} + }, + "created_at": "2016-03-23T18:48:22Z", + "updated_at": "2016-03-23T18:48:42Z", + "links": { + "self": { + "href": "https://api.example.org/v3/processes/6a901b7c-9417-4dc1-8189-d3234aa0ab82" + }, + "scale": { + "href": "https://api.example.org/v3/processes/6a901b7c-9417-4dc1-8189-d3234aa0ab82/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.example.org/v3/apps/ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5" + }, + "space": { + "href": "https://api.example.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576" + }, + "stats": { + "href": "https://api.example.org/v3/processes/6a901b7c-9417-4dc1-8189-d3234aa0ab82/stats" + } + } + }, + { + "guid": "3fccacd9-4b02-4b96-8d02-8e865865e9eb", + "type": "worker", + "command": "[PRIVATE DATA HIDDEN IN LISTS]", + "instances": 1, + "memory_in_mb": 256, + "disk_in_mb": 1024, + "health_check": { + "type": "process", + "data": { + "timeout": null + } + }, + "relationships": { + "app": { + "data": { + "guid": "ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5" + } + }, + "revision": null + }, + "metadata": { + "labels": {}, + "annotations": {} + }, + "created_at": "2016-03-23T18:48:22Z", + "updated_at": "2016-03-23T18:48:42Z", + "links": { + "self": { + "href": "https://api.example.org/v3/processes/3fccacd9-4b02-4b96-8d02-8e865865e9eb" + }, + "scale": { + "href": "https://api.example.org/v3/processes/3fccacd9-4b02-4b96-8d02-8e865865e9eb/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.example.org/v3/apps/ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5" + }, + "space": { + "href": "https://api.example.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576" + }, + "stats": { + "href": "https://api.example.org/v3/processes/3fccacd9-4b02-4b96-8d02-8e865865e9eb/stats" + } + } + } + ] +} \ No newline at end of file diff --git a/test/fixtures/v3/processes/GET_{id}_response.json b/test/fixtures/v3/processes/GET_{id}_response.json new file mode 100644 index 0000000..cf45237 --- /dev/null +++ b/test/fixtures/v3/processes/GET_{id}_response.json @@ -0,0 +1,50 @@ +{ + "guid": "6a901b7c-9417-4dc1-8189-d3234aa0ab82", + "type": "web", + "command": "rackup", + "instances": 5, + "memory_in_mb": 256, + "disk_in_mb": 1024, + "health_check": { + "type": "port", + "data": { + "timeout": null + } + }, + "relationships": { + "app": { + "data": { + "guid": "ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5" + } + }, + "revision": { + "data": { + "guid": "885735b5-aea4-4cf5-8e44-961af0e41920" + } + } + }, + "metadata": { + "labels": { }, + "annotations": { } + }, + "created_at": "2016-03-23T18:48:22Z", + "updated_at": "2016-03-23T18:48:42Z", + "links": { + "self": { + "href": "https://api.example.org/v3/processes/6a901b7c-9417-4dc1-8189-d3234aa0ab82" + }, + "scale": { + "href": "https://api.example.org/v3/processes/6a901b7c-9417-4dc1-8189-d3234aa0ab82/actions/scale", + "method": "POST" + }, + "app": { + "href": "https://api.example.org/v3/apps/ccc25a0f-c8f4-4b39-9f1b-de9f328d0ee5" + }, + "space": { + "href": "https://api.example.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576" + }, + "stats": { + "href": "https://api.example.org/v3/processes/6a901b7c-9417-4dc1-8189-d3234aa0ab82/stats" + } + } +} \ No newline at end of file diff --git a/test/v3/test_processes.py b/test/v3/test_processes.py new file mode 100644 index 0000000..f8e6b15 --- /dev/null +++ b/test/v3/test_processes.py @@ -0,0 +1,32 @@ +import unittest +from http import HTTPStatus + +from abstract_test_case import AbstractTestCase +from cloudfoundry_client.v3.processes import Process + + +class TestProcesses(unittest.TestCase, AbstractTestCase): + @classmethod + def setUpClass(cls): + cls.mock_client_class() + + def setUp(self): + self.build_client() + + def test_list(self): + self.client.get.return_value = self.mock_response("/v3/processes", HTTPStatus.OK, None, "v3", "processes", "GET_response.json") + all_processes = [process for process in self.client.v3.processes.list()] + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual(2, len(all_processes)) + self.assertEqual(all_processes[0]["type"], "web") + self.assertIsInstance(all_processes[0], Process) + + def test_get(self): + self.client.get.return_value = self.mock_response( + "/v3/processes/process_id", HTTPStatus.OK, None, "v3", "processes", "GET_{id}_response.json" + ) + process = self.client.v3.processes.get("process_id") + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual("rackup", process["command"]) + self.assertIsInstance(process, Process) + From 47d2efe97a476dfc535be68d80b898078170bf78 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sun, 20 Jun 2021 21:35:09 +0200 Subject: [PATCH 091/264] Fixes pagination method for v2 entities CLoses #125 --- main/cloudfoundry_client/v3/entities.py | 8 +++-- main/cloudfoundry_client/v3/processes.py | 14 ++------- .../v3/processes/GET_{id}_response.json | 8 ++--- .../v3/processes/GET_{id}_stats_response.json | 30 +++++++++++++++++++ test/v3/test_processes.py | 19 +++++++++--- 5 files changed, 57 insertions(+), 22 deletions(-) create mode 100644 test/fixtures/v3/processes/GET_{id}_stats_response.json diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index 1b50194..631afa7 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -160,10 +160,12 @@ def _paginate(self, url_requested: str, entity_type: Optional[ENTITY_TYPE] = Non response_json = self._read_response(response, JsonObject) for resource in response_json["resources"]: yield self._entity(resource, entity_type) + pagination = response_json.get("pagination") if ( - "next" not in response_json["pagination"] - or response_json["pagination"]["next"] is None - or response_json["pagination"]["next"].get("href") is None + pagination is None + or "next" not in pagination + or pagination["next"] is None + or pagination["next"].get("href") is None ): break else: diff --git a/main/cloudfoundry_client/v3/processes.py b/main/cloudfoundry_client/v3/processes.py index 076ab8e..cb632f3 100644 --- a/main/cloudfoundry_client/v3/processes.py +++ b/main/cloudfoundry_client/v3/processes.py @@ -1,19 +1,11 @@ -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING -from cloudfoundry_client.v3.entities import EntityManager, Entity +from cloudfoundry_client.v3.entities import EntityManager if TYPE_CHECKING: from cloudfoundry_client.client import CloudFoundryClient -class Process(Entity): - @staticmethod - def _manager_method(link_name: str, link_method: str) -> Optional[str]: - if link_name == "stats" and link_method == "get": - return "_get" # instead of _paginate - return Entity._manager_method(link_name, link_method) - - class ProcessManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ProcessManager, self).__init__(target_endpoint, client, "/v3/processes", Process) + super(ProcessManager, self).__init__(target_endpoint, client, "/v3/processes") diff --git a/test/fixtures/v3/processes/GET_{id}_response.json b/test/fixtures/v3/processes/GET_{id}_response.json index cf45237..c416dcb 100644 --- a/test/fixtures/v3/processes/GET_{id}_response.json +++ b/test/fixtures/v3/processes/GET_{id}_response.json @@ -1,5 +1,5 @@ { - "guid": "6a901b7c-9417-4dc1-8189-d3234aa0ab82", + "guid": "process_id", "type": "web", "command": "rackup", "instances": 5, @@ -31,10 +31,10 @@ "updated_at": "2016-03-23T18:48:42Z", "links": { "self": { - "href": "https://api.example.org/v3/processes/6a901b7c-9417-4dc1-8189-d3234aa0ab82" + "href": "https://api.example.org/v3/processes/process_id" }, "scale": { - "href": "https://api.example.org/v3/processes/6a901b7c-9417-4dc1-8189-d3234aa0ab82/actions/scale", + "href": "https://api.example.org/v3/processes/process_id/actions/scale", "method": "POST" }, "app": { @@ -44,7 +44,7 @@ "href": "https://api.example.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576" }, "stats": { - "href": "https://api.example.org/v3/processes/6a901b7c-9417-4dc1-8189-d3234aa0ab82/stats" + "href": "https://api.example.org/v3/processes/process_id/stats" } } } \ No newline at end of file diff --git a/test/fixtures/v3/processes/GET_{id}_stats_response.json b/test/fixtures/v3/processes/GET_{id}_stats_response.json new file mode 100644 index 0000000..74e545b --- /dev/null +++ b/test/fixtures/v3/processes/GET_{id}_stats_response.json @@ -0,0 +1,30 @@ +{ + "resources": [ + { + "type": "web", + "index": 0, + "state": "RUNNING", + "usage": { + "time": "2016-03-23T23:17:30.476314154Z", + "cpu": 0.00038711029163348665, + "mem": 19177472, + "disk": 69705728 + }, + "host": "10.244.16.10", + "instance_ports": [ + { + "external": 64546, + "internal": 8080, + "external_tls_proxy_port": 61002, + "internal_tls_proxy_port": 61003 + } + ], + "uptime": 9042, + "mem_quota": 268435456, + "disk_quota": 1073741824, + "fds_quota": 16384, + "isolation_segment": "example_iso_segment", + "details": null + } + ] +} \ No newline at end of file diff --git a/test/v3/test_processes.py b/test/v3/test_processes.py index f8e6b15..ee501e8 100644 --- a/test/v3/test_processes.py +++ b/test/v3/test_processes.py @@ -2,7 +2,7 @@ from http import HTTPStatus from abstract_test_case import AbstractTestCase -from cloudfoundry_client.v3.processes import Process +from cloudfoundry_client.v3.entities import Entity class TestProcesses(unittest.TestCase, AbstractTestCase): @@ -14,12 +14,13 @@ def setUp(self): self.build_client() def test_list(self): - self.client.get.return_value = self.mock_response("/v3/processes", HTTPStatus.OK, None, "v3", "processes", "GET_response.json") + self.client.get.return_value = self.mock_response("/v3/processes", HTTPStatus.OK, None, "v3", "processes", + "GET_response.json") all_processes = [process for process in self.client.v3.processes.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(2, len(all_processes)) self.assertEqual(all_processes[0]["type"], "web") - self.assertIsInstance(all_processes[0], Process) + self.assertIsInstance(all_processes[0], Entity) def test_get(self): self.client.get.return_value = self.mock_response( @@ -28,5 +29,15 @@ def test_get(self): process = self.client.v3.processes.get("process_id") self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual("rackup", process["command"]) - self.assertIsInstance(process, Process) + self.assertIsInstance(process, Entity) + def test_get_then_stats(self): + get_process = self.mock_response("/v3/processes/process_id", HTTPStatus.OK, None, "v3", "processes", + "GET_{id}_response.json") + get_process_stats = self.mock_response("/v3/processes/process_id/stats", HTTPStatus.OK, None, "v3", "processes", + "GET_{id}_stats_response.json") + self.client.get.side_effect = [get_process, get_process_stats] + process = self.client.v3.processes.get("process_id") + stats = [stat for stat in process.stats()] + self.assertEqual(1, len(stats)) + self.assertEqual(stats[0]["state"], "RUNNING") From 42504d7b56312d0f34100d56fbd9d22e4935b110 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sun, 20 Jun 2021 21:43:48 +0200 Subject: [PATCH 092/264] Prepare version 1.20.1 --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 6c632a8..53d301d 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.20.0" +__version__ = "1.20.1" From 923c2450772f63d699ed6fced34e04921afbc9c9 Mon Sep 17 00:00:00 2001 From: Andy Paine Date: Mon, 21 Jun 2021 12:30:57 +0100 Subject: [PATCH 093/264] Fix relationships with no data/guid The data object for quota is typically null (see example in API docs https://v3-apidocs.cloudfoundry.org/version/3.99.0/#the-space-object). This ends up with the relationships dict containing the `quota` key but the value is None so `dict.get('quota', {})` returns None. Wrapping in braces and using `or` means that we will default to `{}` when either the key doesn't exist (old behaviour) and also when the value of the key is `None` (new behaviour) so the next `.get('guid')` is safe. Also removed the default of `None` from the `.get('guid')` since `None` is the default already --- main/cloudfoundry_client/v3/entities.py | 8 +++----- test/fixtures/v3/apps/GET_response_include_space.json | 5 ++++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index 631afa7..3cca77e 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -149,8 +149,7 @@ def _delete(self, url: str) -> Optional[str]: return None def _list(self, requested_path: str, entity_type: Optional[ENTITY_TYPE] = None, **kwargs) -> PaginateEntities: - url_requested = EntityManager._get_url_with_encoded_params("%s%s" % (self.target_endpoint, requested_path), - **kwargs) + url_requested = EntityManager._get_url_with_encoded_params("%s%s" % (self.target_endpoint, requested_path), **kwargs) for element in self._paginate(url_requested, entity_type): yield element @@ -244,11 +243,10 @@ def _mixin_included_resources(self, result: JsonObject) -> JsonObject: def _include_resources(self, resource: JsonObject, result: JsonObject) -> None: for relationship_name, relationship in resource.get("relationships", {}).items(): - relationship_guid = relationship.get("data", {}).get("guid", None) + relationship_guid = (relationship.get("data") or {}).get("guid") included_resources = result["included"].get(plural(relationship_name), None) if relationship_guid is not None and included_resources is not None: - included_resource = next((r for r in included_resources if relationship_guid == r.get("guid", None)), - None) + included_resource = next((r for r in included_resources if relationship_guid == r.get("guid", None)), None) if included_resource is not None: self._include_resources(included_resource, result) included = resource.setdefault("_included", {}) diff --git a/test/fixtures/v3/apps/GET_response_include_space.json b/test/fixtures/v3/apps/GET_response_include_space.json index 5a6c91b..6f50667 100644 --- a/test/fixtures/v3/apps/GET_response_include_space.json +++ b/test/fixtures/v3/apps/GET_response_include_space.json @@ -151,6 +151,9 @@ "data": { "guid": "e00705b9-7b42-4561-ae97-2520399d2133" } + }, + "quota": { + "data": null } }, "links": { @@ -167,4 +170,4 @@ } ] } -} \ No newline at end of file +} From 0afdb0613a890fbc9207f55766110a951995a1b3 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Mon, 21 Jun 2021 14:54:50 +0200 Subject: [PATCH 094/264] Prepare version 1.20.2 --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 53d301d..cf10b72 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.20.1" +__version__ = "1.20.2" From 6c254293a7704446cfb7eeef5bd7352a1c4b6429 Mon Sep 17 00:00:00 2001 From: Philipp Thun Date: Wed, 23 Jun 2021 09:03:59 +0200 Subject: [PATCH 095/264] Remove unnecessary default value from dict.get --- integration/v2/test_applications.py | 4 ++-- main/cloudfoundry_client/client.py | 4 ++-- .../operations/push/validation/manifest.py | 2 +- main/cloudfoundry_client/v3/entities.py | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/integration/v2/test_applications.py b/integration/v2/test_applications.py index e717c8a..33a2bec 100644 --- a/integration/v2/test_applications.py +++ b/integration/v2/test_applications.py @@ -48,8 +48,8 @@ def test_list(self): self.assertEqual(e.body.get("error_code"), "CF-AppStoppedStatsError") env = client.v2.apps.get_env(application["metadata"]["guid"]) self.assertIsNotNone(env) - self.assertIsNotNone(env.get("application_env_json", None)) - self.assertIsNotNone(env["application_env_json"].get("VCAP_APPLICATION", None)) + self.assertIsNotNone(env.get("application_env_json")) + self.assertIsNotNone(env["application_env_json"].get("VCAP_APPLICATION")) self.assertGreater(len(env["application_env_json"]["VCAP_APPLICATION"].get("application_uris", [])), 0) _logger.debug("env = %s", json.dumps(env)) cpt += 1 diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 0a43132..6070978 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -205,7 +205,7 @@ def _is_token_expired(response: Response) -> bool: try: json_data = response.json() invalid_token_error = "CF-InvalidAuthToken" - if json_data.get("errors", None) is not None: # V3 error response + if json_data.get("errors") is not None: # V3 error response for error in json_data.get("errors"): if error.get("code", 0) == 1000 and error.get("title", "") == invalid_token_error: _logger.info("_is_token_v3_expired - true") @@ -295,4 +295,4 @@ def _check_response(response: Response) -> Response: body = response.json() except ValueError: body = response.text - raise InvalidStatusCode(HTTPStatus(response.status_code), body, response.headers.get("x-vcap-request-id", None)) + raise InvalidStatusCode(HTTPStatus(response.status_code), body, response.headers.get("x-vcap-request-id")) diff --git a/main/cloudfoundry_client/operations/push/validation/manifest.py b/main/cloudfoundry_client/operations/push/validation/manifest.py index de4666e..94c93dc 100644 --- a/main/cloudfoundry_client/operations/push/validation/manifest.py +++ b/main/cloudfoundry_client/operations/push/validation/manifest.py @@ -126,7 +126,7 @@ def _absolute_path(manifest_directory: str, manifest: dict): @staticmethod def _convert_environment(app_manifest: dict): - environment = app_manifest.get("env", None) + environment = app_manifest.get("env") if environment is not None: if type(environment) != dict: raise AssertionError("'env' entry must be a dictionary") diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index 3cca77e..89293ab 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -244,9 +244,9 @@ def _mixin_included_resources(self, result: JsonObject) -> JsonObject: def _include_resources(self, resource: JsonObject, result: JsonObject) -> None: for relationship_name, relationship in resource.get("relationships", {}).items(): relationship_guid = (relationship.get("data") or {}).get("guid") - included_resources = result["included"].get(plural(relationship_name), None) + included_resources = result["included"].get(plural(relationship_name)) if relationship_guid is not None and included_resources is not None: - included_resource = next((r for r in included_resources if relationship_guid == r.get("guid", None)), None) + included_resource = next((r for r in included_resources if relationship_guid == r.get("guid")), None) if included_resource is not None: self._include_resources(included_resource, result) included = resource.setdefault("_included", {}) From 156903fe79b5fd4dcf330c1ad228ab128d9fcfb6 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Wed, 23 Jun 2021 09:17:17 +0200 Subject: [PATCH 096/264] Prepare version 1.20.3 --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index cf10b72..916162a 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.20.2" +__version__ = "1.20.3" From d370d52a0267ca5554738b6a61805f1da8021af9 Mon Sep 17 00:00:00 2001 From: Andy Paine Date: Wed, 21 Jul 2021 16:51:34 +0100 Subject: [PATCH 097/264] Add organization quota manager + methods This mostly behaves like a normal Entity manager with some additional requirements around updating quota amounts and organizations separately. Using @dataclasses allows for an easy way to take structured arguments for POSTing to CC and should add some helpful type hinting --- README.rst | 1 + main/cloudfoundry_client/client.py | 2 + main/cloudfoundry_client/main/main.py | 9 ++ .../v3/organization_quotas.py | 93 ++++++++++++ .../v3/organization_quotas/GET_response.json | 88 ++++++++++++ .../GET_{id}_response.json | 38 +++++ .../PATCH_{id}_response.json | 38 +++++ .../v3/organization_quotas/POST_response.json | 38 +++++ .../POST_{id}_organizations_response.json | 18 +++ test/v3/test_organization_quotas.py | 136 ++++++++++++++++++ 10 files changed, 461 insertions(+) create mode 100644 main/cloudfoundry_client/v3/organization_quotas.py create mode 100644 test/fixtures/v3/organization_quotas/GET_response.json create mode 100644 test/fixtures/v3/organization_quotas/GET_{id}_response.json create mode 100644 test/fixtures/v3/organization_quotas/PATCH_{id}_response.json create mode 100644 test/fixtures/v3/organization_quotas/POST_response.json create mode 100644 test/fixtures/v3/organization_quotas/POST_{id}_organizations_response.json create mode 100644 test/v3/test_organization_quotas.py diff --git a/README.rst b/README.rst index bbbab8a..4a25f7a 100644 --- a/README.rst +++ b/README.rst @@ -240,6 +240,7 @@ Available managers on API V3 are: - ``domains`` - ``feature-flags`` - ``organizations`` +- ``organization_quotas`` - ``service_instances`` - ``spaces`` - ``tasks`` diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 6070978..f36d3f8 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -29,6 +29,7 @@ from cloudfoundry_client.v3.domains import DomainManager from cloudfoundry_client.v3.feature_flags import FeatureFlagManager from cloudfoundry_client.v3.isolation_segments import IsolationSegmentManager +from cloudfoundry_client.v3.organization_quotas import OrganizationQuotaManager from cloudfoundry_client.v3.processes import ProcessManager from cloudfoundry_client.v3.organizations import OrganizationManager from cloudfoundry_client.v3.service_brokers import ServiceBrokerManager @@ -101,6 +102,7 @@ def __init__(self, target_endpoint: str, credential_manager: "CloudFoundryClient self.isolation_segments = IsolationSegmentManager(target_endpoint, credential_manager) self.jobs = JobManager(target_endpoint, credential_manager) self.organizations = OrganizationManager(target_endpoint, credential_manager) + self.organization_quotas = OrganizationQuotaManager(target_endpoint, credential_manager) self.processes = ProcessManager(target_endpoint, credential_manager) self.service_brokers = ServiceBrokerManager(target_endpoint, credential_manager) self.service_instances = ServiceInstanceManager(target_endpoint, credential_manager) diff --git a/main/cloudfoundry_client/main/main.py b/main/cloudfoundry_client/main/main.py index 0fc2045..7f06253 100644 --- a/main/cloudfoundry_client/main/main.py +++ b/main/cloudfoundry_client/main/main.py @@ -180,6 +180,15 @@ def main(): allow_creation=True, allow_deletion=True, ), + CommandDomain( + display_name="OrganizationQuotas", + entity_name="organization_quota", + api_version="v3", + filter_list_parameters=["names", "guids", "organization_guids"], + allow_retrieve_by_name=True, + allow_creation=True, + allow_deletion=True, + ), CommandDomain( display_name="Spaces", entity_name="space", diff --git a/main/cloudfoundry_client/v3/organization_quotas.py b/main/cloudfoundry_client/v3/organization_quotas.py new file mode 100644 index 0000000..c84c9f4 --- /dev/null +++ b/main/cloudfoundry_client/v3/organization_quotas.py @@ -0,0 +1,93 @@ +from typing import List, Optional, TYPE_CHECKING +from dataclasses import dataclass, asdict + +from cloudfoundry_client.v3.entities import Entity, EntityManager, ToManyRelationship + +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + + +@dataclass +class AppsQuota: + total_memory_in_mb: int + per_process_memory_in_mb: int + total_instances: int + per_app_tasks: int + + +@dataclass +class ServicesQuota: + paid_services_allowed: bool + total_service_instances: int + total_service_keys: int + + +@dataclass +class RoutesQuota: + total_routes: int + total_reserved_ports: int + + +@dataclass +class DomainsQuota: + total_domains: int + + +class OrganizationQuotaManager(EntityManager): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super().__init__(target_endpoint, client, "/v3/organization_quotas") + + def remove(self, guid: str): + super()._remove(guid) + + def create( + self, + name: str, + apps_quota: Optional[AppsQuota] = None, + services_quota: Optional[ServicesQuota] = None, + routes_quota: Optional[RoutesQuota] = None, + domains_quota: Optional[DomainsQuota] = None, + assigned_organizations: Optional[List[str]] = None, + ) -> Entity: + data = self._asdict(name, apps_quota, services_quota, routes_quota, domains_quota, assigned_organizations) + return super()._create(data) + + def update( + self, + guid: str, + name: str, + apps_quota: Optional[AppsQuota] = None, + services_quota: Optional[ServicesQuota] = None, + routes_quota: Optional[RoutesQuota] = None, + domains_quota: Optional[DomainsQuota] = None, + ) -> Entity: + data = self._asdict(name, apps_quota, services_quota, routes_quota, domains_quota) + return super()._update(guid, data) + + def apply_to_organizations(self, guid: str, organization_guids: List[str]) -> ToManyRelationship: + data = ToManyRelationship(*organization_guids) + return ToManyRelationship.from_json_object( + super()._post("%s%s/%s/relationships/organizations" % (self.target_endpoint, self.entity_uri, guid), data=data) + ) + + def _asdict( + self, + name: str, + apps_quota: Optional[AppsQuota] = None, + services_quota: Optional[ServicesQuota] = None, + routes_quota: Optional[RoutesQuota] = None, + domains_quota: Optional[DomainsQuota] = None, + assigned_organizations: Optional[List[str]] = None, + ): + data = {"name": name} + if apps_quota: + data["apps"] = asdict(apps_quota) + if services_quota: + data["services"] = asdict(services_quota) + if routes_quota: + data["routes"] = asdict(routes_quota) + if domains_quota: + data["domains"] = asdict(domains_quota) + if assigned_organizations: + data["relationships"] = {"organizations": assigned_organizations} + return data diff --git a/test/fixtures/v3/organization_quotas/GET_response.json b/test/fixtures/v3/organization_quotas/GET_response.json new file mode 100644 index 0000000..d57f146 --- /dev/null +++ b/test/fixtures/v3/organization_quotas/GET_response.json @@ -0,0 +1,88 @@ +{ + "pagination": { + "total_results": 2, + "total_pages": 1, + "first": { + "href": "https://api.example.org/v3/organization_quotas?page=1&per_page=50" + }, + "last": { + "href": "https://api.example.org/v3/organization_quotas?page=1&per_page=50" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "quota-1-guid", + "created_at": "2016-05-04T17:00:41Z", + "updated_at": "2016-05-04T17:00:41Z", + "name": "don-quixote", + "apps": { + "total_memory_in_mb": 5120, + "per_process_memory_in_mb": 1024, + "total_instances": 10, + "per_app_tasks": 5 + }, + "services": { + "paid_services_allowed": true, + "total_service_instances": 10, + "total_service_keys": 20 + }, + "routes": { + "total_routes": 8, + "total_reserved_ports": 4 + }, + "domains": { + "total_domains": 7 + }, + "relationships": { + "organizations": { + "data": [ + { + "guid": "9b370018-c38e-44c9-86d6-155c76801104" + } + ] + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/organization_quotas/quota-1-guid" + } + } + }, + { + "guid": "quota-2-guid", + "created_at": "2017-05-04T17:00:41Z", + "updated_at": "2017-05-04T17:00:41Z", + "name": "sancho-panza", + "apps": { + "total_memory_in_mb": 2048, + "per_process_memory_in_mb": 1024, + "total_instances": 5, + "per_app_tasks": 2 + }, + "services": { + "paid_services_allowed": true, + "total_service_instances": 10, + "total_service_keys": 20 + }, + "routes": { + "total_routes": 8, + "total_reserved_ports": 4 + }, + "domains": { + "total_domains": 7 + }, + "relationships": { + "organizations": { + "data": [] + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/organization_quotas/quota-2-guid" + } + } + } + ] +} \ No newline at end of file diff --git a/test/fixtures/v3/organization_quotas/GET_{id}_response.json b/test/fixtures/v3/organization_quotas/GET_{id}_response.json new file mode 100644 index 0000000..0840545 --- /dev/null +++ b/test/fixtures/v3/organization_quotas/GET_{id}_response.json @@ -0,0 +1,38 @@ +{ + "guid": "quota-guid", + "created_at": "2016-05-04T17:00:41Z", + "updated_at": "2016-05-04T17:00:41Z", + "name": "don-quixote", + "apps": { + "total_memory_in_mb": 5120, + "per_process_memory_in_mb": 1024, + "total_instances": 10, + "per_app_tasks": 5 + }, + "services": { + "paid_services_allowed": true, + "total_service_instances": 10, + "total_service_keys": 20 + }, + "routes": { + "total_routes": 8, + "total_reserved_ports": 4 + }, + "domains": { + "total_domains": 7 + }, + "relationships": { + "organizations": { + "data": [ + { + "guid": "9b370018-c38e-44c9-86d6-155c76801104" + } + ] + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/organization_quotas/quota-guid" + } + } +} \ No newline at end of file diff --git a/test/fixtures/v3/organization_quotas/PATCH_{id}_response.json b/test/fixtures/v3/organization_quotas/PATCH_{id}_response.json new file mode 100644 index 0000000..0840545 --- /dev/null +++ b/test/fixtures/v3/organization_quotas/PATCH_{id}_response.json @@ -0,0 +1,38 @@ +{ + "guid": "quota-guid", + "created_at": "2016-05-04T17:00:41Z", + "updated_at": "2016-05-04T17:00:41Z", + "name": "don-quixote", + "apps": { + "total_memory_in_mb": 5120, + "per_process_memory_in_mb": 1024, + "total_instances": 10, + "per_app_tasks": 5 + }, + "services": { + "paid_services_allowed": true, + "total_service_instances": 10, + "total_service_keys": 20 + }, + "routes": { + "total_routes": 8, + "total_reserved_ports": 4 + }, + "domains": { + "total_domains": 7 + }, + "relationships": { + "organizations": { + "data": [ + { + "guid": "9b370018-c38e-44c9-86d6-155c76801104" + } + ] + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/organization_quotas/quota-guid" + } + } +} \ No newline at end of file diff --git a/test/fixtures/v3/organization_quotas/POST_response.json b/test/fixtures/v3/organization_quotas/POST_response.json new file mode 100644 index 0000000..0840545 --- /dev/null +++ b/test/fixtures/v3/organization_quotas/POST_response.json @@ -0,0 +1,38 @@ +{ + "guid": "quota-guid", + "created_at": "2016-05-04T17:00:41Z", + "updated_at": "2016-05-04T17:00:41Z", + "name": "don-quixote", + "apps": { + "total_memory_in_mb": 5120, + "per_process_memory_in_mb": 1024, + "total_instances": 10, + "per_app_tasks": 5 + }, + "services": { + "paid_services_allowed": true, + "total_service_instances": 10, + "total_service_keys": 20 + }, + "routes": { + "total_routes": 8, + "total_reserved_ports": 4 + }, + "domains": { + "total_domains": 7 + }, + "relationships": { + "organizations": { + "data": [ + { + "guid": "9b370018-c38e-44c9-86d6-155c76801104" + } + ] + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/organization_quotas/quota-guid" + } + } +} \ No newline at end of file diff --git a/test/fixtures/v3/organization_quotas/POST_{id}_organizations_response.json b/test/fixtures/v3/organization_quotas/POST_{id}_organizations_response.json new file mode 100644 index 0000000..a937d7c --- /dev/null +++ b/test/fixtures/v3/organization_quotas/POST_{id}_organizations_response.json @@ -0,0 +1,18 @@ +{ + "data": [ + { + "guid": "org-guid1" + }, + { + "guid": "org-guid2" + }, + { + "guid": "previous-org-guid" + } + ], + "links": { + "self": { + "href": "https://api.example.org/v3/organization_quotas/quota-guid/relationships/organizations" + } + } +} \ No newline at end of file diff --git a/test/v3/test_organization_quotas.py b/test/v3/test_organization_quotas.py new file mode 100644 index 0000000..3ebb3fc --- /dev/null +++ b/test/v3/test_organization_quotas.py @@ -0,0 +1,136 @@ +import sys +import unittest +from http import HTTPStatus +from unittest.mock import patch + +import cloudfoundry_client.main.main as main +from abstract_test_case import AbstractTestCase +from cloudfoundry_client.v3.entities import Entity, ToManyRelationship +from cloudfoundry_client.v3.organization_quotas import AppsQuota, DomainsQuota, RoutesQuota, ServicesQuota + + +class TestOrganizationQuotas(unittest.TestCase, AbstractTestCase): + @classmethod + def setUpClass(cls): + cls.mock_client_class() + + def setUp(self): + self.build_client() + + def test_list(self): + self.client.get.return_value = self.mock_response( + "/v3/organization_quotas", HTTPStatus.OK, None, "v3", "organization_quotas", "GET_response.json" + ) + all_quotas = [quota for quota in self.client.v3.organization_quotas.list()] + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual(2, len(all_quotas)) + self.assertEqual(all_quotas[0]["name"], "don-quixote") + self.assertIsInstance(all_quotas[0], Entity) + + def test_get(self): + self.client.get.return_value = self.mock_response( + "/v3/organization_quotas/quota-guid", HTTPStatus.OK, None, "v3", "organization_quotas", "GET_{id}_response.json" + ) + quota = self.client.v3.organization_quotas.get("quota-guid") + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual("don-quixote", quota["name"]) + self.assertIsInstance(quota, Entity) + + def test_update(self): + self.client.patch.return_value = self.mock_response( + "/v3/organization_quotas/quota_id", HTTPStatus.OK, None, "v3", "organization_quotas", "PATCH_{id}_response.json" + ) + result = self.client.v3.organization_quotas.update( + "quota_id", + "don-quixote", + apps_quota=AppsQuota(total_memory_in_mb=5120, per_process_memory_in_mb=1024, total_instances=10, per_app_tasks=5), + services_quota=ServicesQuota(paid_services_allowed=True, total_service_instances=10, total_service_keys=20), + routes_quota=RoutesQuota(total_routes=8, total_reserved_ports=4), + domains_quota=DomainsQuota(total_domains=7), + ) + self.client.patch.assert_called_with( + self.client.patch.return_value.url, + json={ + "name": "don-quixote", + "apps": {"total_memory_in_mb": 5120, "per_process_memory_in_mb": 1024, "total_instances": 10, "per_app_tasks": 5}, + "services": {"paid_services_allowed": True, "total_service_instances": 10, "total_service_keys": 20}, + "routes": {"total_routes": 8, "total_reserved_ports": 4}, + "domains": {"total_domains": 7}, + }, + ) + self.assertIsNotNone(result) + + def test_create(self): + self.client.post.return_value = self.mock_response( + "/v3/organization_quotas", HTTPStatus.OK, None, "v3", "organization_quotas", "POST_response.json" + ) + result = self.client.v3.organization_quotas.create( + "don-quixote", + apps_quota=AppsQuota(total_memory_in_mb=5120, per_process_memory_in_mb=1024, total_instances=10, per_app_tasks=5), + services_quota=ServicesQuota(paid_services_allowed=True, total_service_instances=10, total_service_keys=20), + routes_quota=RoutesQuota(total_routes=8, total_reserved_ports=4), + domains_quota=DomainsQuota(total_domains=7), + assigned_organizations=["assigned-org"], + ) + self.client.post.assert_called_with( + self.client.post.return_value.url, + files=None, + json={ + "name": "don-quixote", + "apps": {"total_memory_in_mb": 5120, "per_process_memory_in_mb": 1024, "total_instances": 10, "per_app_tasks": 5}, + "services": {"paid_services_allowed": True, "total_service_instances": 10, "total_service_keys": 20}, + "routes": {"total_routes": 8, "total_reserved_ports": 4}, + "domains": {"total_domains": 7}, + "relationships": {"organizations": ["assigned-org"]}, + }, + ) + self.assertIsNotNone(result) + + def test_apply_to_organizations(self): + self.client.post.return_value = self.mock_response( + "/v3/organization_quotas/quota_id/relationships/organizations", + HTTPStatus.OK, + None, + "v3", + "organization_quotas", + "POST_{id}_organizations_response.json", + ) + result = self.client.v3.organization_quotas.apply_to_organizations( + "quota_id", + organization_guids=["org-guid1", "org-guid2"], + ) + self.client.post.assert_called_with( + self.client.post.return_value.url, files=None, json={"data": [{"guid": "org-guid1"}, {"guid": "org-guid2"}]} + ) + self.assertIsInstance(result, ToManyRelationship) + # Endpoint adds to existing list of orgs so 1 existing + 2 new + self.assertEqual(3, len(result.guids)) + self.assertIsNotNone(result["links"]) + + def test_remove(self): + self.client.delete.return_value = self.mock_response("/v3/organization_quotas/quota_id", HTTPStatus.NO_CONTENT, None) + self.client.v3.organization_quotas.remove("quota_id") + self.client.delete.assert_called_with(self.client.delete.return_value.url) + + @patch.object(sys, "argv", ["main", "list_organization_quotas"]) + def test_main_list_organization_quotas(self): + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.get.return_value = self.mock_response( + "/v3/organization_quotas", HTTPStatus.OK, None, "v3", "organization_quotas", "GET_response.json" + ) + main.main() + self.client.get.assert_called_with(self.client.get.return_value.url) + + @patch.object(sys, "argv", ["main", "get_organization_quota", "1d3bf0ec-5806-43c4-b64e-8364dba1086a"]) + def test_main_get_organization_quotas(self): + with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): + self.client.get.return_value = self.mock_response( + "/v3/organization_quotas/1d3bf0ec-5806-43c4-b64e-8364dba1086a", + HTTPStatus.OK, + None, + "v3", + "organization_quotas", + "GET_{id}_response.json", + ) + main.main() + self.client.get.assert_called_with(self.client.get.return_value.url) From 87185e3d16d06fba1b1544e2dc5cfc5d06b0baaf Mon Sep 17 00:00:00 2001 From: Andy Paine Date: Fri, 23 Jul 2021 12:22:04 +0100 Subject: [PATCH 098/264] Add other missing managers from README These exist on the V3 object and were just missing from the README --- README.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 4a25f7a..efd1195 100644 --- a/README.rst +++ b/README.rst @@ -238,10 +238,16 @@ Available managers on API V3 are: - ``apps`` - ``buildpacks`` - ``domains`` -- ``feature-flags`` +- ``feature_flags`` +- ``isolation_segments`` +- ``jobs`` - ``organizations`` - ``organization_quotas`` +- ``processes`` +- ``service_brokers`` - ``service_instances`` +- ``service_offerings`` +- ``service_plans`` - ``spaces`` - ``tasks`` From c6518c59faaaaf92c92d7feff7eff8c7e14ae95f Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sat, 24 Jul 2021 20:44:32 +0200 Subject: [PATCH 099/264] Deduce login information from links Attempt to get login info from * login section if available * uaa if available * self as fallback Close #135 --- main/cloudfoundry_client/client.py | 9 ++++++--- test/test_client.py | 4 +++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 6070978..e4f709b 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -181,8 +181,7 @@ def rlpgateway(self): return self._rlpgateway - @staticmethod - def _get_info(target_endpoint: str, proxy: Optional[dict] = None, verify: bool = True) -> Info: + def _get_info(self, target_endpoint: str, proxy: Optional[dict] = None, verify: bool = True) -> Info: root_response = CloudFoundryClient._check_response( requests.get("%s/" % target_endpoint, proxies=proxy if proxy is not None else dict(http="", https=""), verify=verify) ) @@ -193,12 +192,16 @@ def _get_info(target_endpoint: str, proxy: Optional[dict] = None, verify: bool = log_stream = root_links.get("log_stream") return Info( root_links["cloud_controller_v2"]["meta"]["version"], - root_links["login"]["href"], + self._resolve_login_endpoint(root_links), target_endpoint, logging.get("href") if logging is not None else None, log_stream.get("href") if log_stream is not None else None, ) + @staticmethod + def _resolve_login_endpoint(root_links): + return (root_links.get("login") or root_links.get("uaa") or root_links.get("self"))["href"] + @staticmethod def _is_token_expired(response: Response) -> bool: if response.status_code == HTTPStatus.UNAUTHORIZED.value: diff --git a/test/test_client.py b/test/test_client.py index e0a5e26..7d659e8 100644 --- a/test/test_client.py +++ b/test/test_client.py @@ -129,7 +129,9 @@ def test_get_info(self): ): requests.Session.return_value = session self._mock_info_calls(requests) - info = CloudFoundryClient._get_info(self.TARGET_ENDPOINT) + client = CloudFoundryClient(self.TARGET_ENDPOINT) + self._mock_info_calls(requests) + info = client._get_info(self.TARGET_ENDPOINT) self.assertEqual(info.api_endpoint, self.TARGET_ENDPOINT) self.assertEqual(info.api_v2_version, self.API_V2_VERSION) self.assertEqual(info.doppler_endpoint, self.DOPPLER_ENDPOINT) From 64fa5187933fd54a6c5cc5d8b558d8eefdcdbf60 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sun, 25 Jul 2021 10:10:58 +0200 Subject: [PATCH 100/264] Prepare version 1.20.4 --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 916162a..1e7c29e 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.20.3" +__version__ = "1.20.4" From 4a6ca541ad28a0988d03f897fb0f8b18b7a22931 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Tue, 27 Jul 2021 17:01:53 +0200 Subject: [PATCH 101/264] Fixes bad code example in README Closes #138 --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index bbbab8a..618338a 100644 --- a/README.rst +++ b/README.rst @@ -221,11 +221,11 @@ The following code snippet issues three requests to the API in order to get app, .. code-block:: python app = client.v3.apps.get("app-guid") - print("App name: %s" % app["name]) + print("App name: %s" % app["name"]) space = app.space() - print("Space name: %s" % space["name]) + print("Space name: %s" % space["name"]) org = space.organization() - print("Org name: %s" % org["name]) + print("Org name: %s" % org["name"]) By changing the first line only, a single request fetches all the data. The navigation from app to space and space to organization remains unchanged. From cd7ee1fff75bc4fb5115cb0d7fae6585ce80a228 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Fri, 6 Aug 2021 17:32:49 +0200 Subject: [PATCH 102/264] Implement manager for 'service_credential_bindings' v3 resources Closes #139 --- main/cloudfoundry_client/client.py | 2 + .../v3/service_credential_bindings.py | 21 ++++ .../GET_response.json | 100 ++++++++++++++++++ .../GET_{id}_details_response.json | 10 ++ .../GET_{id}_parameters_response.json | 4 + .../GET_{id}_response.json | 50 +++++++++ test/v3/test_service_credential_bindings.py | 83 +++++++++++++++ 7 files changed, 270 insertions(+) create mode 100644 main/cloudfoundry_client/v3/service_credential_bindings.py create mode 100644 test/fixtures/v3/service_credential_bindings/GET_response.json create mode 100644 test/fixtures/v3/service_credential_bindings/GET_{id}_details_response.json create mode 100644 test/fixtures/v3/service_credential_bindings/GET_{id}_parameters_response.json create mode 100644 test/fixtures/v3/service_credential_bindings/GET_{id}_response.json create mode 100644 test/v3/test_service_credential_bindings.py diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 098dca2..e035887 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -33,6 +33,7 @@ from cloudfoundry_client.v3.processes import ProcessManager from cloudfoundry_client.v3.organizations import OrganizationManager from cloudfoundry_client.v3.service_brokers import ServiceBrokerManager +from cloudfoundry_client.v3.service_credential_bindings import ServiceCredentialBindingManager from cloudfoundry_client.v3.service_instances import ServiceInstanceManager from cloudfoundry_client.v3.service_offerings import ServiceOfferingsManager from cloudfoundry_client.v3.service_plans import ServicePlanManager @@ -105,6 +106,7 @@ def __init__(self, target_endpoint: str, credential_manager: "CloudFoundryClient self.organization_quotas = OrganizationQuotaManager(target_endpoint, credential_manager) self.processes = ProcessManager(target_endpoint, credential_manager) self.service_brokers = ServiceBrokerManager(target_endpoint, credential_manager) + self.service_credential_bindings = ServiceCredentialBindingManager(target_endpoint, credential_manager) self.service_instances = ServiceInstanceManager(target_endpoint, credential_manager) self.service_offerings = ServiceOfferingsManager(target_endpoint, credential_manager) self.service_plans = ServicePlanManager(target_endpoint, credential_manager) diff --git a/main/cloudfoundry_client/v3/service_credential_bindings.py b/main/cloudfoundry_client/v3/service_credential_bindings.py new file mode 100644 index 0000000..fb56c6c --- /dev/null +++ b/main/cloudfoundry_client/v3/service_credential_bindings.py @@ -0,0 +1,21 @@ +from typing import TYPE_CHECKING, Optional + +from cloudfoundry_client.v3.entities import EntityManager, Entity + +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + + +class ServiceCredentialBinding(Entity): + @staticmethod + def _manager_method(link_name: str, link_method: str) -> Optional[str]: + if (link_name == "details" or link_name == "parameters") and link_method == "get": + return "_get" # instead of _paginate + return Entity._manager_method(link_name, link_method) + + +class ServiceCredentialBindingManager(EntityManager): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(ServiceCredentialBindingManager, self).__init__(target_endpoint, client, + "/v3/service_credential_bindings", + ServiceCredentialBinding) diff --git a/test/fixtures/v3/service_credential_bindings/GET_response.json b/test/fixtures/v3/service_credential_bindings/GET_response.json new file mode 100644 index 0000000..108f612 --- /dev/null +++ b/test/fixtures/v3/service_credential_bindings/GET_response.json @@ -0,0 +1,100 @@ +{ + "pagination": { + "total_results": 2, + "total_pages": 1, + "first": { + "href": "https://api.example.org/v3/service_credential_bindings?page=1&per_page=2" + }, + "last": { + "href": "https://api.example.org/v3/service_credential_bindings?page=2&per_page=2" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "dde5ad2a-d8f4-44dc-a56f-0452d744f1c3", + "created_at": "2015-11-13T17:02:56Z", + "updated_at": "2016-06-08T16:41:26Z", + "name": "some-binding-name", + "type": "app", + "last_operation": { + "type": "create", + "state": "succeeded", + "created_at": "2015-11-13T17:02:56Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "metadata": { + "annotations": { + "foo": "bar" + }, + "labels": { + "baz": "qux" + } + }, + "relationships": { + "app": { + "data": { + "guid": "74f7c078-0934-470f-9883-4fddss5b8f13" + } + }, + "service_instance": { + "data": { + "guid": "8bfe4c1b-9e18-45b1-83be-124163f31f9e" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/service_credential_bindings/dde5ad2a-d8f4-44dc-a56f-0452d744f1c3" + }, + "details": { + "href": "https://api.example.org/v3/service_credential_bindings/dde5ad2a-d8f4-44dc-a56f-0452d744f1c3/details" + }, + "service_instance": { + "href": "https://api.example.org/v3/service_instances/8bfe4c1b-9e18-45b1-83be-124163f31f9e" + }, + "app": { + "href": "https://api.example.org/v3/apps/74f7c078-0934-470f-9883-4fddss5b8f13" + } + } + }, + { + "guid": "7aa37bad-6ccb-4ef9-ba48-9ce3a91b2b62", + "created_at": "2015-11-13T17:02:56Z", + "updated_at": "2016-06-08T16:41:26Z", + "name": "some-key-name", + "type": "key", + "last_operation": { + "type": "create", + "state": "succeeded", + "created_at": "2015-11-13T17:02:56Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "metadata": { + "annotations": { + "foo": "bar" + }, + "labels": { } + }, + "relationships": { + "service_instance": { + "data": { + "guid": "8bfe4c1b-9e18-45b1-83be-124163f31f9e" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/service_credential_bindings/7aa37bad-6ccb-4ef9-ba48-9ce3a91b2b62" + }, + "details": { + "href": "https://api.example.org/v3/service_credential_bindings/7aa37bad-6ccb-4ef9-ba48-9ce3a91b2b62/details" + }, + "service_instance": { + "href": "https://api.example.org/v3/service_instances/8bf356j3-9e18-45b1-3333-124163f31f9e" + } + } + } + ] +} \ No newline at end of file diff --git a/test/fixtures/v3/service_credential_bindings/GET_{id}_details_response.json b/test/fixtures/v3/service_credential_bindings/GET_{id}_details_response.json new file mode 100644 index 0000000..415f896 --- /dev/null +++ b/test/fixtures/v3/service_credential_bindings/GET_{id}_details_response.json @@ -0,0 +1,10 @@ +{ + "credentials": { + "connection": "mydb://user@password:example.com" + }, + "syslog_drain_url": "http://syslog.example.com/drain", + "volume_mounts": [ + "/vcap/data", + "store" + ] +} diff --git a/test/fixtures/v3/service_credential_bindings/GET_{id}_parameters_response.json b/test/fixtures/v3/service_credential_bindings/GET_{id}_parameters_response.json new file mode 100644 index 0000000..4fddbe3 --- /dev/null +++ b/test/fixtures/v3/service_credential_bindings/GET_{id}_parameters_response.json @@ -0,0 +1,4 @@ +{ + "foo": "bar", + "foz": "baz" +} \ No newline at end of file diff --git a/test/fixtures/v3/service_credential_bindings/GET_{id}_response.json b/test/fixtures/v3/service_credential_bindings/GET_{id}_response.json new file mode 100644 index 0000000..d9a4e38 --- /dev/null +++ b/test/fixtures/v3/service_credential_bindings/GET_{id}_response.json @@ -0,0 +1,50 @@ +{ + "guid": "dde5ad2a-d8f4-44dc-a56f-0452d744f1c3", + "created_at": "2015-11-13T17:02:56Z", + "updated_at": "2016-06-08T16:41:26Z", + "name": "some-name", + "type": "app", + "last_operation": { + "type": "create", + "state": "succeeded", + "created_at": "2015-11-13T17:02:56Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "metadata": { + "annotations": { + "foo": "bar" + }, + "labels": { + "baz": "qux" + } + }, + "relationships": { + "app": { + "data": { + "guid": "74f7c078-0934-470f-9883-4fddss5b8f13" + } + }, + "service_instance": { + "data": { + "guid": "8bfe4c1b-9e18-45b1-83be-124163f31f9e" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/service_credential_bindings/dde5ad2a-d8f4-44dc-a56f-0452d744f1c3" + }, + "details": { + "href": "https://api.example.org/v3/service_credential_bindings/dde5ad2a-d8f4-44dc-a56f-0452d744f1c3/details" + }, + "parameters": { + "href": "https://api.example.org/v3/service_credential_bindings/dde5ad2a-d8f4-44dc-a56f-0452d744f1c3/parameters" + }, + "service_instance": { + "href": "https://api.example.org/v3/service_instances/8bfe4c1b-9e18-45b1-83be-124163f31f9e" + }, + "app": { + "href": "https://api.example.org/v3/apps/74f7c078-0934-470f-9883-4fddss5b8f13" + } + } +} \ No newline at end of file diff --git a/test/v3/test_service_credential_bindings.py b/test/v3/test_service_credential_bindings.py new file mode 100644 index 0000000..6191c91 --- /dev/null +++ b/test/v3/test_service_credential_bindings.py @@ -0,0 +1,83 @@ +import unittest +from http import HTTPStatus + +from abstract_test_case import AbstractTestCase +from cloudfoundry_client.v3.service_credential_bindings import ServiceCredentialBinding + + +class TestCredentialBindings(unittest.TestCase, AbstractTestCase): + @classmethod + def setUpClass(cls): + cls.mock_client_class() + + def setUp(self): + self.build_client() + + def test_list(self): + self.client.get.return_value = self.mock_response( + "/v3/service_credential_bindings", HTTPStatus.OK, None, "v3", "service_credential_bindings", + "GET_response.json" + ) + all_service_credential_bindings = [credential_binding for credential_binding in + self.client.v3.service_credential_bindings.list()] + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual(2, len(all_service_credential_bindings)) + self.assertEqual(all_service_credential_bindings[0]["name"], "some-binding-name") + for domain in all_service_credential_bindings: + self.assertIsInstance(domain, ServiceCredentialBinding) + + def test_get(self): + self.client.get.return_value = self.mock_response( + "/v3/service_credential_bindings/service_credential_binding_id", + HTTPStatus.OK, + None, + "v3", + "service_credential_bindings", "GET_{id}_response.json" + ) + result = self.client.v3.service_credential_bindings.get("service_credential_binding_id") + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertIsNotNone(result) + self.assertIsInstance(result, ServiceCredentialBinding) + + def test_get_then_details(self): + get_service_credential_binding = self.mock_response( + "/v3/service_credential_bindings/service_credential_binding_id", + HTTPStatus.OK, + None, + "v3", + "service_credential_bindings", + "GET_{id}_response.json") + get_details = self.mock_response( + "/v3/service_credential_bindings/service_credential_binding_id/details", + HTTPStatus.OK, + None, + "v3", + "service_credential_bindings", + "GET_{id}_details_response.json", + ) + self.client.get.side_effect = [get_service_credential_binding, get_details] + service_credential_binding = self.client.v3.service_credential_bindings.get("service_credential_binding_id") + details = service_credential_binding.details() + self.assertIsInstance(details, dict) + self.assertEqual("mydb://user@password:example.com", details["credentials"]["connection"]) + + def test_get_then_parameters(self): + get_service_credential_binding = self.mock_response( + "/v3/service_credential_bindings/service_credential_binding_id", + HTTPStatus.OK, + None, "v3", + "service_credential_bindings", + "GET_{id}_response.json") + get_parameters = self.mock_response( + "/v3/service_credential_bindings/service_credential_binding_id/parameters", + HTTPStatus.OK, + None, + "v3", + "service_credential_bindings", + "GET_{id}_parameters_response.json", + ) + self.client.get.side_effect = [get_service_credential_binding, get_parameters] + service_credential_binding = self.client.v3.service_credential_bindings.get("service_credential_binding_id") + parameters = service_credential_binding.parameters() + self.assertIsInstance(parameters, dict) + self.assertEqual("bar", parameters["foo"]) From a4016c9c64ad79f60f55684a746b8b2a9f3ea1cd Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sun, 8 Aug 2021 16:17:16 +0200 Subject: [PATCH 103/264] Added 'service_credential_bindings' to V3 managers in README --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index 4dd8a32..280831f 100644 --- a/README.rst +++ b/README.rst @@ -245,6 +245,7 @@ Available managers on API V3 are: - ``organization_quotas`` - ``processes`` - ``service_brokers`` +- ``service_credential_bindings`` - ``service_instances`` - ``service_offerings`` - ``service_plans`` From dfaaef3057f62c51d5dc68716b5c33a66f2a8b6c Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sun, 8 Aug 2021 16:17:39 +0200 Subject: [PATCH 104/264] Prepare version 1.21.0 --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 1e7c29e..5ac7ee2 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.20.4" +__version__ = "1.21.0" From c66f000d04045a3da8c34aa55752060ec17c2ff0 Mon Sep 17 00:00:00 2001 From: Katharina Przybill <30441792+kathap@users.noreply.github.com> Date: Fri, 20 Aug 2021 11:30:43 +0200 Subject: [PATCH 105/264] Change check_success state to FAILED or COMPLETE - Due to the PPOLLING state, the only state of waiting is not any more only PROCESSING, but PROCESSING and POLLING. So we want to check if state is FAILED or COMPELETE to be sure it finished. --- main/cloudfoundry_client/v3/jobs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/v3/jobs.py b/main/cloudfoundry_client/v3/jobs.py index 783bd12..0ae6d84 100644 --- a/main/cloudfoundry_client/v3/jobs.py +++ b/main/cloudfoundry_client/v3/jobs.py @@ -32,7 +32,7 @@ def wait_for_job_completion( step_function=step_function, poll_forever=poll_forever, timeout=timeout, - check_success=lambda job: job["state"] != "PROCESSING", + check_success=lambda job: job["state"] == "FAILED" or job["state"] == "COMPLETE", ) except polling2.TimeoutException as e: raise JobTimeout(e) From 3cc33ab9461d3fb3e5475389d58ba2986a3b56bd Mon Sep 17 00:00:00 2001 From: Adil Ilhan Date: Sun, 22 Aug 2021 21:19:55 +0200 Subject: [PATCH 106/264] Update functionality has been added to the V3 ServiceInstanceManager. The unit test of the update function of V3 ServiceInstanceManager has been added. --- .../v3/service_instances.py | 32 +++++++++++++++++ test/v3/test_service_instances.py | 35 +++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/main/cloudfoundry_client/v3/service_instances.py b/main/cloudfoundry_client/v3/service_instances.py index 449a2ae..70a3d14 100644 --- a/main/cloudfoundry_client/v3/service_instances.py +++ b/main/cloudfoundry_client/v3/service_instances.py @@ -38,5 +38,37 @@ def create( data["metadata"] = metadata return super(ServiceInstanceManager, self)._create(data) + def update( + self, + instance_guid: str, + name: Optional[str] = None, + parameters: Optional[dict] = None, + service_plan: Optional[str] = None, + maintenance_info: Optional[str] = None, + meta_labels: Optional[dict] = None, + meta_annotations: Optional[dict] = None, + tags: Optional[List[str]] = None + ) -> Entity: + data = {} + if name: + data["name"] = name + if parameters: + data["parameters"] = parameters + if service_plan: + data["relationships"] = { + "service_plan": ToOneRelationship(service_plan)} + if maintenance_info: + data["maintenance_info"] = {"version": maintenance_info} + if tags: + data["tags"] = tags + if meta_labels or meta_annotations: + metadata = dict() + if meta_labels: + metadata["labels"] = meta_labels + if meta_annotations: + metadata["annotations"] = meta_annotations + data["metadata"] = metadata + return super(ServiceInstanceManager, self)._update(instance_guid, data) + def remove(self, guid: str, asynchronous: bool = True): super(ServiceInstanceManager, self)._remove(guid, asynchronous) diff --git a/test/v3/test_service_instances.py b/test/v3/test_service_instances.py index 05615e1..a7e1cb2 100644 --- a/test/v3/test_service_instances.py +++ b/test/v3/test_service_instances.py @@ -44,6 +44,41 @@ def test_create(self): self.assertIsNotNone(result) self.assertIsInstance(result, Entity) + def test_update(self): + self.client.patch.return_value = self.mock_response( + "/v3/service_instances/instance-guid-123", + HTTPStatus.ACCEPTED, + headers={"Location": "https://somewhere.org/v3/jobs/job_id"}, + ) + result = self.client.v3.service_instances.update( + instance_guid="instance-guid-123", + name="space-name", + parameters={"foo": "bar"}, + service_plan="custom_service_plan", + maintenance_info="1.2.3", + meta_labels={"foo": "bar"}, + meta_annotations={"foo": "bar"}, + tags=["mytag", "myothertag"], + ) + + self.client.patch.assert_called_with( + self.client.patch.return_value.url, + json={ + "name": "space-name", + "parameters": {"foo": "bar"}, + "relationships": { + "service_plan": { + "data": {"guid": "custom_service_plan"}}, + }, + "maintenance_info": {"version": "1.2.3"}, + "tags": ["mytag", "myothertag"], + "metadata": {'labels': {'foo': 'bar'}, + 'annotations': {'foo': 'bar'}} + }, + ) + self.assertIsNotNone(result) + self.assertIsInstance(result, Entity) + def test_list(self): self.client.get.return_value = self.mock_response( "/v3/service_instances", HTTPStatus.OK, None, "v3", "service_instances", "GET_response.json" From 81190e8283c8e4906b05b67b13e6f6ebb4d7c2a0 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Mon, 23 Aug 2021 19:49:30 +0200 Subject: [PATCH 107/264] Prepare version 1.22.0 --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 5ac7ee2..2012de3 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.21.0" +__version__ = "1.22.0" From b38ae4a1598fdd0ad8a48111f92a0f6e49da19fe Mon Sep 17 00:00:00 2001 From: johha <45264872+johha@users.noreply.github.com> Date: Wed, 1 Sep 2021 08:49:27 +0200 Subject: [PATCH 108/264] Add missing dependency for py 3.6 Co-authored-by: Marc Misoch --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index d7fa2ca..de07b36 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,3 +5,4 @@ websocket-client==0.54.0 PyYAML==5.4.1 requests>=2.5.0 polling2==0.4.6 +dataclasses==0.8; python_version < '3.7' From c7579c3c20bfe55ad1b4492301ed4e88cd669c9d Mon Sep 17 00:00:00 2001 From: Adil Ilhan Date: Tue, 24 Aug 2021 01:09:52 +0200 Subject: [PATCH 109/264] Restart endpoint added to V3 AppManager Pipeline fixed --- main/cloudfoundry_client/v3/apps.py | 5 +++++ test/v3/test_apps.py | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/main/cloudfoundry_client/v3/apps.py b/main/cloudfoundry_client/v3/apps.py index 29c3160..85641fd 100644 --- a/main/cloudfoundry_client/v3/apps.py +++ b/main/cloudfoundry_client/v3/apps.py @@ -19,6 +19,11 @@ class AppManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(AppManager, self).__init__(target_endpoint, client, "/v3/apps", App) + def restart(self, application_guid: str): + return super(AppManager, self)._post("%s%s/%s/actions/restart" % (self.target_endpoint, + self.entity_uri, + application_guid)) + def remove(self, application_guid: str): super(AppManager, self)._remove(application_guid) diff --git a/test/v3/test_apps.py b/test/v3/test_apps.py index d018af7..98e642e 100644 --- a/test/v3/test_apps.py +++ b/test/v3/test_apps.py @@ -73,6 +73,16 @@ def test_get_then_environment_variables(self): self.assertIsInstance(environment_variables, dict) self.assertEqual("production", environment_variables["var"]["RAILS_ENV"]) + def test_restart(self): + self.client.post.return_value = self.mock_response( + "/v3/apps/app_id/actions/restart", HTTPStatus.OK, None, "v3", "apps", + "GET_{id}_response.json" + ) + + app = self.client.v3.apps.restart("app_id") + self.assertIsInstance(app, JsonObject) + self.assertEquals("my_app", app["name"]) + def test_remove(self): self.client.delete.return_value = self.mock_response("/v3/apps/app_id", HTTPStatus.NO_CONTENT, None) self.client.v3.apps.remove("app_id") From 684aae676140c66bbc0815f1d448a879dd7f6cf2 Mon Sep 17 00:00:00 2001 From: Andy Paine Date: Mon, 6 Sep 2021 14:26:59 +0100 Subject: [PATCH 110/264] Fix relationships in domain create https://v3-apidocs.cloudfoundry.org/version/3.99.0/#create-a-domain show that the organization/shared_organizations should be embedded in a `relationships` field. I've added to the test some data for the shared_organizations to make sure they serialize correctly too Signed-off-by: Andy Paine --- main/cloudfoundry_client/v3/domains.py | 6 ++++-- test/v3/test_domains.py | 13 ++++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/main/cloudfoundry_client/v3/domains.py b/main/cloudfoundry_client/v3/domains.py index 51338c1..0702290 100644 --- a/main/cloudfoundry_client/v3/domains.py +++ b/main/cloudfoundry_client/v3/domains.py @@ -34,8 +34,10 @@ def create( data = { "name": name, "internal": internal, - "organization": organization, - "shared_organizations": shared_organizations, + "relationships": { + "organization": organization, + "shared_organizations": shared_organizations, + }, "metadata": {"labels": meta_labels, "annotations": meta_annotations}, } return super(DomainManager, self)._create(data) diff --git a/test/v3/test_domains.py b/test/v3/test_domains.py index fb971a4..4e6c8dc 100644 --- a/test/v3/test_domains.py +++ b/test/v3/test_domains.py @@ -57,7 +57,7 @@ def test_create(self): "domain_id", internal=False, organization=ToOneRelationship("organization-guid"), - shared_organizations=None, + shared_organizations=ToManyRelationship("other-org-guid-1", "other-org-guid-2"), meta_labels=None, meta_annotations=None, ) @@ -67,8 +67,15 @@ def test_create(self): json={ "name": "domain_id", "internal": False, - "organization": {"data": {"guid": "organization-guid"}}, - "shared_organizations": None, + "relationships": { + "organization": {"data": {"guid": "organization-guid"}}, + "shared_organizations": { + "data": [ + {"guid": "other-org-guid-1"}, + {"guid": "other-org-guid-2"}, + ] + }, + }, "metadata": {"labels": None, "annotations": None}, }, ) From dac6aef7f719e18ca377c36a191aa36248b53be2 Mon Sep 17 00:00:00 2001 From: Andy Paine Date: Mon, 6 Sep 2021 14:56:23 +0100 Subject: [PATCH 111/264] Fix relationships in organization_quotas The ToManyRelationship helper does the hard work here so use that instead of the List[str] --- main/cloudfoundry_client/v3/organization_quotas.py | 13 +++++++------ test/v3/test_organization_quotas.py | 6 +++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/main/cloudfoundry_client/v3/organization_quotas.py b/main/cloudfoundry_client/v3/organization_quotas.py index c84c9f4..23a6bb7 100644 --- a/main/cloudfoundry_client/v3/organization_quotas.py +++ b/main/cloudfoundry_client/v3/organization_quotas.py @@ -1,4 +1,4 @@ -from typing import List, Optional, TYPE_CHECKING +from typing import Optional, TYPE_CHECKING from dataclasses import dataclass, asdict from cloudfoundry_client.v3.entities import Entity, EntityManager, ToManyRelationship @@ -47,7 +47,7 @@ def create( services_quota: Optional[ServicesQuota] = None, routes_quota: Optional[RoutesQuota] = None, domains_quota: Optional[DomainsQuota] = None, - assigned_organizations: Optional[List[str]] = None, + assigned_organizations: Optional[ToManyRelationship] = None, ) -> Entity: data = self._asdict(name, apps_quota, services_quota, routes_quota, domains_quota, assigned_organizations) return super()._create(data) @@ -64,10 +64,11 @@ def update( data = self._asdict(name, apps_quota, services_quota, routes_quota, domains_quota) return super()._update(guid, data) - def apply_to_organizations(self, guid: str, organization_guids: List[str]) -> ToManyRelationship: - data = ToManyRelationship(*organization_guids) + def apply_to_organizations(self, guid: str, organizations: ToManyRelationship) -> ToManyRelationship: return ToManyRelationship.from_json_object( - super()._post("%s%s/%s/relationships/organizations" % (self.target_endpoint, self.entity_uri, guid), data=data) + super()._post( + "%s%s/%s/relationships/organizations" % (self.target_endpoint, self.entity_uri, guid), data=organizations + ) ) def _asdict( @@ -77,7 +78,7 @@ def _asdict( services_quota: Optional[ServicesQuota] = None, routes_quota: Optional[RoutesQuota] = None, domains_quota: Optional[DomainsQuota] = None, - assigned_organizations: Optional[List[str]] = None, + assigned_organizations: Optional[ToManyRelationship] = None, ): data = {"name": name} if apps_quota: diff --git a/test/v3/test_organization_quotas.py b/test/v3/test_organization_quotas.py index 3ebb3fc..c7cc0f8 100644 --- a/test/v3/test_organization_quotas.py +++ b/test/v3/test_organization_quotas.py @@ -70,7 +70,7 @@ def test_create(self): services_quota=ServicesQuota(paid_services_allowed=True, total_service_instances=10, total_service_keys=20), routes_quota=RoutesQuota(total_routes=8, total_reserved_ports=4), domains_quota=DomainsQuota(total_domains=7), - assigned_organizations=["assigned-org"], + assigned_organizations=ToManyRelationship("assigned-org"), ) self.client.post.assert_called_with( self.client.post.return_value.url, @@ -81,7 +81,7 @@ def test_create(self): "services": {"paid_services_allowed": True, "total_service_instances": 10, "total_service_keys": 20}, "routes": {"total_routes": 8, "total_reserved_ports": 4}, "domains": {"total_domains": 7}, - "relationships": {"organizations": ["assigned-org"]}, + "relationships": {"organizations": {"data": [{"guid": "assigned-org"}]}}, }, ) self.assertIsNotNone(result) @@ -97,7 +97,7 @@ def test_apply_to_organizations(self): ) result = self.client.v3.organization_quotas.apply_to_organizations( "quota_id", - organization_guids=["org-guid1", "org-guid2"], + organizations=ToManyRelationship("org-guid1", "org-guid2"), ) self.client.post.assert_called_with( self.client.post.return_value.url, files=None, json={"data": [{"guid": "org-guid1"}, {"guid": "org-guid2"}]} From 77c090ce0ff0bdfd37c0e18485f9455b6f96ef6c Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Thu, 9 Sep 2021 16:33:32 +0200 Subject: [PATCH 112/264] Prepare version 1.23.0 --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 2012de3..ecf03ed 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.22.0" +__version__ = "1.23.0" From fd74c5c751e67509c3061b29f0b996891cdc96b5 Mon Sep 17 00:00:00 2001 From: Joseph Beeson Date: Fri, 15 Oct 2021 19:26:35 +0200 Subject: [PATCH 113/264] EntityManager check if result uses pagination --- main/cloudfoundry_client/v3/entities.py | 29 +++++++++++++++---------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index 89293ab..768980b 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -157,19 +157,24 @@ def _paginate(self, url_requested: str, entity_type: Optional[ENTITY_TYPE] = Non response = self.client.get(url_requested) while True: response_json = self._read_response(response, JsonObject) - for resource in response_json["resources"]: - yield self._entity(resource, entity_type) - pagination = response_json.get("pagination") - if ( - pagination is None - or "next" not in pagination - or pagination["next"] is None - or pagination["next"].get("href") is None - ): - break + if "resources" in response_json: + for resource in response_json["resources"]: + yield self._entity(resource, entity_type) + + pagination = response_json.get("pagination") + if ( + pagination is None + or "next" not in pagination + or pagination["next"] is None + or pagination["next"].get("href") is None + ): + break + else: + url_requested = response_json["pagination"]["next"]["href"] + response = self.client.get(url_requested) + else: - url_requested = response_json["pagination"]["next"]["href"] - response = self.client.get(url_requested) + yield self._entity(response_json, entity_type) def _create(self, data: dict) -> Entity: url = "%s%s" % (self.target_endpoint, self.entity_uri) From dc016caa23bc9a0a8081499158192016578a4ed8 Mon Sep 17 00:00:00 2001 From: Joseph Beeson Date: Mon, 18 Oct 2021 10:18:54 +0200 Subject: [PATCH 114/264] EneityManager add missing break --- main/cloudfoundry_client/v3/entities.py | 1 + 1 file changed, 1 insertion(+) diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index 768980b..b4c2b8e 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -175,6 +175,7 @@ def _paginate(self, url_requested: str, entity_type: Optional[ENTITY_TYPE] = Non else: yield self._entity(response_json, entity_type) + break def _create(self, data: dict) -> Entity: url = "%s%s" % (self.target_endpoint, self.entity_uri) From 251b0bc86ba111df724e3abc298f28b634c7e03e Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Mon, 18 Oct 2021 12:35:18 +0200 Subject: [PATCH 115/264] Adding test for credentials link in v3 service instances --- .../v3/service_instances/GET_response.json | 35 +++++++++++++++---- .../GET_{id}_credentials_response.json | 5 +++ .../service_instances/GET_{id}_response.json | 35 +++++++++++++++---- test/v3/test_service_instances.py | 21 +++++++++-- 4 files changed, 82 insertions(+), 14 deletions(-) create mode 100644 test/fixtures/v3/service_instances/GET_{id}_credentials_response.json diff --git a/test/fixtures/v3/service_instances/GET_response.json b/test/fixtures/v3/service_instances/GET_response.json index 8ef7d21..51e5d0b 100644 --- a/test/fixtures/v3/service_instances/GET_response.json +++ b/test/fixtures/v3/service_instances/GET_response.json @@ -13,14 +13,25 @@ }, "resources": [ { - "guid": "85ccdcad-d725-4109-bca4-fd6ba062b5c8", - "created_at": "2017-11-17T13:54:21Z", - "updated_at": "2017-11-17T13:54:21Z", - "name": "my_service_instance", + "guid": "88ce23e5-27c3-4381-a2df-32a28ec43133", + "created_at": "2020-03-10T15:56:08Z", + "updated_at": "2020-03-10T15:56:08Z", + "last_operation": { + "type": "create", + "state": "succeeded", + "description": "Operation succeeded", + "updated_at": "2020-03-10T15:49:32Z", + "created_at": "2020-03-10T15:49:29Z" + }, + "name": "my-user-provided-instance", + "tags": ["sql"], + "type": "user-provided", + "syslog_drain_url": "http://logs.com", + "route_service_url": "https://routes.com", "relationships": { "space": { "data": { - "guid": "ae0031f9-dd49-461c-a945-df40e77c39cb" + "guid": "5a84d315-9513-4d74-95e5-f6a5501eeef7" } } }, @@ -29,8 +40,20 @@ "annotations": {} }, "links": { + "self": { + "href": "https://api.example.org/v3/service_instances/88ce23e5-27c3-4381-a2df-32a28ec43133" + }, "space": { - "href": "https://api.example.org/v3/spaces/ae0031f9-dd49-461c-a945-df40e77c39cb" + "href": "https://api.example.org/v3/spaces/5a84d315-9513-4d74-95e5-f6a5501eeef7" + }, + "credentials": { + "href": "https://api.example.org/v3/service_instances/88ce23e5-27c3-4381-a2df-32a28ec43133/credentials" + }, + "service_credential_bindings": { + "href": "https://api.example.org/v3/service_credential_bindings?service_instance_guids=88ce23e5-27c3-4381-a2df-32a28ec43133" + }, + "service_route_bindings": { + "href": "https://api.example.org/v3/service_route_bindings?service_instance_guids=88ce23e5-27c3-4381-a2df-32a28ec43133" } } } diff --git a/test/fixtures/v3/service_instances/GET_{id}_credentials_response.json b/test/fixtures/v3/service_instances/GET_{id}_credentials_response.json new file mode 100644 index 0000000..36e4a89 --- /dev/null +++ b/test/fixtures/v3/service_instances/GET_{id}_credentials_response.json @@ -0,0 +1,5 @@ +{ + "username": "my-username", + "password": "super-secret", + "other": "credential" +} diff --git a/test/fixtures/v3/service_instances/GET_{id}_response.json b/test/fixtures/v3/service_instances/GET_{id}_response.json index 9c1e643..6a476fc 100644 --- a/test/fixtures/v3/service_instances/GET_{id}_response.json +++ b/test/fixtures/v3/service_instances/GET_{id}_response.json @@ -1,12 +1,23 @@ { - "guid": "85ccdcad-d725-4109-bca4-fd6ba062b5c8", - "created_at": "2017-11-17T13:54:21Z", - "updated_at": "2017-11-17T13:54:21Z", - "name": "my_service_instance", + "guid": "service_instance_id", + "created_at": "2020-03-10T15:56:08Z", + "updated_at": "2020-03-10T15:56:08Z", + "last_operation": { + "type": "create", + "state": "succeeded", + "description": "Operation succeeded", + "updated_at": "2020-03-10T15:49:32Z", + "created_at": "2020-03-10T15:49:29Z" + }, + "name": "my-user-provided-instance", + "tags": ["sql"], + "type": "user-provided", + "syslog_drain_url": "http://logs.com", + "route_service_url": "https://routes.com", "relationships": { "space": { "data": { - "guid": "ae0031f9-dd49-461c-a945-df40e77c39cb" + "guid": "5a84d315-9513-4d74-95e5-f6a5501eeef7" } } }, @@ -15,8 +26,20 @@ "annotations": {} }, "links": { + "self": { + "href": "https://api.example.org/v3/service_instances/service_instance_id" + }, "space": { - "href": "https://api.example.org/v3/spaces/ae0031f9-dd49-461c-a945-df40e77c39cb" + "href": "https://api.example.org/v3/spaces/5a84d315-9513-4d74-95e5-f6a5501eeef7" + }, + "credentials": { + "href": "https://api.example.org/v3/service_instances/service_instance_id/credentials" + }, + "service_credential_bindings": { + "href": "https://api.example.org/v3/service_credential_bindings?service_instance_guids=service_instance_id" + }, + "service_route_bindings": { + "href": "https://api.example.org/v3/service_route_bindings?service_instance_guids=service_instance_id" } } } \ No newline at end of file diff --git a/test/v3/test_service_instances.py b/test/v3/test_service_instances.py index a7e1cb2..f2a23b2 100644 --- a/test/v3/test_service_instances.py +++ b/test/v3/test_service_instances.py @@ -86,7 +86,7 @@ def test_list(self): all_service_instances = [service_instance for service_instance in self.client.v3.service_instances.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(1, len(all_service_instances)) - self.assertEqual(all_service_instances[0]["guid"], "85ccdcad-d725-4109-bca4-fd6ba062b5c8") + self.assertEqual(all_service_instances[0]["guid"], "88ce23e5-27c3-4381-a2df-32a28ec43133") self.assertIsInstance(all_service_instances[0], Entity) def test_get(self): @@ -95,9 +95,26 @@ def test_get(self): ) service_instance = self.client.v3.service_instances.get("service_instance_id") self.client.get.assert_called_with(self.client.get.return_value.url) - self.assertEqual("85ccdcad-d725-4109-bca4-fd6ba062b5c8", service_instance["guid"]) + self.assertEqual("service_instance_id", service_instance["guid"]) self.assertIsInstance(service_instance, Entity) + def test_get_then_credentials(self): + get_service_instance = self.mock_response( + "/v3/service_instances/service_instance_id", HTTPStatus.OK, None, "v3", "service_instances", "GET_{id}_response.json") + get_credentials = self.mock_response( + "/v3/service_credential_bindings/service_instance_id/credentials", + HTTPStatus.OK, + None, + "v3", + "service_instances", + "GET_{id}_credentials_response.json", + ) + self.client.get.side_effect = [get_service_instance, get_credentials] + service_instance = self.client.v3.service_instances.get("service_instance_id") + credentials = service_instance.credentials() + self.assertIsInstance(credentials, dict) + self.assertEqual("super-secret", credentials["password"]) + def test_remove_user_provided_service_instance(self): self.client.delete.return_value = self.mock_response( "/v3/service_instances/service_instance_id", HTTPStatus.NO_CONTENT, None From fcfeccc6b439a7033e4f58301aee608205fb9c3b Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Mon, 18 Oct 2021 12:22:20 +0200 Subject: [PATCH 116/264] Fixes pagination issue When using links, some links may be plural yet be a single object. This change attempts to return the appropriate object * a generator when 'resources' is in the body * a json object otherwise See #149 #150 #151 --- main/cloudfoundry_client/v3/apps.py | 14 ++----- main/cloudfoundry_client/v3/entities.py | 38 ++++++++++--------- .../v3/service_credential_bindings.py | 15 ++------ test/v3/test_service_credential_bindings.py | 6 +-- 4 files changed, 30 insertions(+), 43 deletions(-) diff --git a/main/cloudfoundry_client/v3/apps.py b/main/cloudfoundry_client/v3/apps.py index 85641fd..2a11b56 100644 --- a/main/cloudfoundry_client/v3/apps.py +++ b/main/cloudfoundry_client/v3/apps.py @@ -1,23 +1,15 @@ -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from cloudfoundry_client.json_object import JsonObject -from cloudfoundry_client.v3.entities import EntityManager, Entity +from cloudfoundry_client.v3.entities import EntityManager if TYPE_CHECKING: from cloudfoundry_client.client import CloudFoundryClient -class App(Entity): - @staticmethod - def _manager_method(link_name: str, link_method: str) -> Optional[str]: - if link_name == "environment_variables" and link_method == "get": - return "_get" # instead of _paginate - return Entity._manager_method(link_name, link_method) - - class AppManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(AppManager, self).__init__(target_endpoint, client, "/v3/apps", App) + super(AppManager, self).__init__(target_endpoint, client, "/v3/apps") def restart(self, application_guid: str): return super(AppManager, self)._post("%s%s/%s/actions/restart" % (self.target_endpoint, diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index b4c2b8e..2b26672 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -67,7 +67,7 @@ def default_method(m, u): def _manager_method(link_name: str, link_method: str) -> Optional[str]: if link_method == "get": if link_name.endswith("s"): - return "_paginate" + return "_attempt_to_paginate" else: return "_get" elif link_method == "post": @@ -154,28 +154,32 @@ def _list(self, requested_path: str, entity_type: Optional[ENTITY_TYPE] = None, yield element def _paginate(self, url_requested: str, entity_type: Optional[ENTITY_TYPE] = None) -> PaginateEntities: - response = self.client.get(url_requested) - while True: - response_json = self._read_response(response, JsonObject) - if "resources" in response_json: - for resource in response_json["resources"]: - yield self._entity(resource, entity_type) + response_json = self._read_response(self.client.get(url_requested), JsonObject) + yield from self._crawl_pagination(entity_type, response_json) + + def _attempt_to_paginate(self, url_requested: str, entity_type: Optional[ENTITY_TYPE] = None) \ + -> Union[PaginateEntities, Entity]: + response_json = self._read_response(self.client.get(url_requested), JsonObject) + if "resources" in response_json: + return self._crawl_pagination(entity_type, response_json) + else: + return response_json - pagination = response_json.get("pagination") - if ( + def _crawl_pagination(self, entity_type, response_json) -> PaginateEntities: + while True: + for resource in response_json["resources"]: + yield self._entity(resource, entity_type) + pagination = response_json.get("pagination") + if ( pagination is None or "next" not in pagination or pagination["next"] is None or pagination["next"].get("href") is None - ): - break - else: - url_requested = response_json["pagination"]["next"]["href"] - response = self.client.get(url_requested) - - else: - yield self._entity(response_json, entity_type) + ): break + else: + url_requested = response_json["pagination"]["next"]["href"] + response_json = self._read_response(self.client.get(url_requested), JsonObject) def _create(self, data: dict) -> Entity: url = "%s%s" % (self.target_endpoint, self.entity_uri) diff --git a/main/cloudfoundry_client/v3/service_credential_bindings.py b/main/cloudfoundry_client/v3/service_credential_bindings.py index fb56c6c..ad343f5 100644 --- a/main/cloudfoundry_client/v3/service_credential_bindings.py +++ b/main/cloudfoundry_client/v3/service_credential_bindings.py @@ -1,21 +1,12 @@ -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING -from cloudfoundry_client.v3.entities import EntityManager, Entity +from cloudfoundry_client.v3.entities import EntityManager if TYPE_CHECKING: from cloudfoundry_client.client import CloudFoundryClient -class ServiceCredentialBinding(Entity): - @staticmethod - def _manager_method(link_name: str, link_method: str) -> Optional[str]: - if (link_name == "details" or link_name == "parameters") and link_method == "get": - return "_get" # instead of _paginate - return Entity._manager_method(link_name, link_method) - - class ServiceCredentialBindingManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(ServiceCredentialBindingManager, self).__init__(target_endpoint, client, - "/v3/service_credential_bindings", - ServiceCredentialBinding) + "/v3/service_credential_bindings") diff --git a/test/v3/test_service_credential_bindings.py b/test/v3/test_service_credential_bindings.py index 6191c91..6ec8bfc 100644 --- a/test/v3/test_service_credential_bindings.py +++ b/test/v3/test_service_credential_bindings.py @@ -2,7 +2,7 @@ from http import HTTPStatus from abstract_test_case import AbstractTestCase -from cloudfoundry_client.v3.service_credential_bindings import ServiceCredentialBinding +from cloudfoundry_client.v3.entities import Entity class TestCredentialBindings(unittest.TestCase, AbstractTestCase): @@ -24,7 +24,7 @@ def test_list(self): self.assertEqual(2, len(all_service_credential_bindings)) self.assertEqual(all_service_credential_bindings[0]["name"], "some-binding-name") for domain in all_service_credential_bindings: - self.assertIsInstance(domain, ServiceCredentialBinding) + self.assertIsInstance(domain, Entity) def test_get(self): self.client.get.return_value = self.mock_response( @@ -37,7 +37,7 @@ def test_get(self): result = self.client.v3.service_credential_bindings.get("service_credential_binding_id") self.client.get.assert_called_with(self.client.get.return_value.url) self.assertIsNotNone(result) - self.assertIsInstance(result, ServiceCredentialBinding) + self.assertIsInstance(result, Entity) def test_get_then_details(self): get_service_credential_binding = self.mock_response( From 11710deabed3fc937333998ebb37caf50c9392b2 Mon Sep 17 00:00:00 2001 From: Alina Golovchenko Date: Tue, 14 Dec 2021 09:29:01 +0100 Subject: [PATCH 117/264] Pin aiohttp>=3.8.0 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index de07b36..bcb1885 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -aiohttp==3.7.4 +aiohttp>=3.8.0 protobuf>=3.6.1 oauth2-client==1.2.1 websocket-client==0.54.0 From ffb7a934d3f2eb7a6bafdd70031504f3c1c4c2fc Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Thu, 16 Dec 2021 19:08:12 +0100 Subject: [PATCH 118/264] Prepare version 1.24.0 --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index ecf03ed..f55ae21 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.23.0" +__version__ = "1.24.0" From 9fedccea2a1b274e1dd4c4396b47f6d91aec6c94 Mon Sep 17 00:00:00 2001 From: Jochen Ehret Date: Wed, 22 Dec 2021 11:23:43 +0100 Subject: [PATCH 119/264] Documentation for /v3/service_plans/:guid/visibility * short documentation that points out the difference between PATCH (replaces/overwrites orgs) and POST (appends orgs) --- main/cloudfoundry_client/v3/service_plans.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/main/cloudfoundry_client/v3/service_plans.py b/main/cloudfoundry_client/v3/service_plans.py index e80c266..257c3bf 100644 --- a/main/cloudfoundry_client/v3/service_plans.py +++ b/main/cloudfoundry_client/v3/service_plans.py @@ -30,6 +30,8 @@ def remove(self, guid: str): def get_visibility(self, service_plan_guid: str) -> Dict: return super(ServicePlanManager, self)._get(f"{self.target_endpoint}{self.entity_uri}/{service_plan_guid}/visibility") + # Updates a service plan visibility. It behaves similar to the POST service plan visibility endpoint but + # this endpoint will REPLACE the existing list of organizations when the service plan is organization visible. def update_visibility(self, service_plan_guid: str, type: str, organizations: Optional[List[dict]] = None) -> Dict: payload = {"type": type} if organizations: @@ -38,6 +40,8 @@ def update_visibility(self, service_plan_guid: str, type: str, organizations: Op url=f"{self.target_endpoint}{self.entity_uri}/{service_plan_guid}/visibility", data=payload ) + # Applies a service plan visibility. It behaves similar to the PATCH service plan visibility endpoint but + # this endpoint will APPEND to the existing list of organizations when the service plan is organization visible. def apply_visibility_to_extra_orgs(self, service_plan_guid: str, organizations: List[dict]) -> Dict: payload = {"type": "organization", "organizations": organizations} return super(ServicePlanManager, self)._post( From 4ccc6fa23f40a58205a7a67e21ec0a86525cf8f9 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sat, 1 Jan 2022 13:31:54 +0100 Subject: [PATCH 120/264] Implement security groups in V3 api Close #156 --- README.rst | 1 + main/cloudfoundry_client/client.py | 2 + .../cloudfoundry_client/v3/security_groups.py | 115 +++++++++++++ .../v3/security_groups/GET_response.json | 84 +++++++++ .../v3/security_groups/GET_{id}_response.json | 44 +++++ .../security_groups/PATCH_{id}_response.json | 44 +++++ .../v3/security_groups/POST_response.json | 44 +++++ ...relationships_running_spaces_response.json | 18 ++ ...relationships_staging_spaces_response.json | 18 ++ test/v3/test_security_groups.py | 160 ++++++++++++++++++ 10 files changed, 530 insertions(+) create mode 100644 main/cloudfoundry_client/v3/security_groups.py create mode 100644 test/fixtures/v3/security_groups/GET_response.json create mode 100644 test/fixtures/v3/security_groups/GET_{id}_response.json create mode 100644 test/fixtures/v3/security_groups/PATCH_{id}_response.json create mode 100644 test/fixtures/v3/security_groups/POST_response.json create mode 100644 test/fixtures/v3/security_groups/POST_{id}_relationships_running_spaces_response.json create mode 100644 test/fixtures/v3/security_groups/POST_{id}_relationships_staging_spaces_response.json create mode 100644 test/v3/test_security_groups.py diff --git a/README.rst b/README.rst index 280831f..6644346 100644 --- a/README.rst +++ b/README.rst @@ -244,6 +244,7 @@ Available managers on API V3 are: - ``organizations`` - ``organization_quotas`` - ``processes`` +- ``security_groups`` - ``service_brokers`` - ``service_credential_bindings`` - ``service_instances`` diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index e035887..ef3a3ab 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -32,6 +32,7 @@ from cloudfoundry_client.v3.organization_quotas import OrganizationQuotaManager from cloudfoundry_client.v3.processes import ProcessManager from cloudfoundry_client.v3.organizations import OrganizationManager +from cloudfoundry_client.v3.security_groups import SecurityGroupManager from cloudfoundry_client.v3.service_brokers import ServiceBrokerManager from cloudfoundry_client.v3.service_credential_bindings import ServiceCredentialBindingManager from cloudfoundry_client.v3.service_instances import ServiceInstanceManager @@ -105,6 +106,7 @@ def __init__(self, target_endpoint: str, credential_manager: "CloudFoundryClient self.organizations = OrganizationManager(target_endpoint, credential_manager) self.organization_quotas = OrganizationQuotaManager(target_endpoint, credential_manager) self.processes = ProcessManager(target_endpoint, credential_manager) + self.security_groups = SecurityGroupManager(target_endpoint, credential_manager) self.service_brokers = ServiceBrokerManager(target_endpoint, credential_manager) self.service_credential_bindings = ServiceCredentialBindingManager(target_endpoint, credential_manager) self.service_instances = ServiceInstanceManager(target_endpoint, credential_manager) diff --git a/main/cloudfoundry_client/v3/security_groups.py b/main/cloudfoundry_client/v3/security_groups.py new file mode 100644 index 0000000..d539474 --- /dev/null +++ b/main/cloudfoundry_client/v3/security_groups.py @@ -0,0 +1,115 @@ +from dataclasses import dataclass, asdict +from enum import Enum, auto +from typing import TYPE_CHECKING, Optional, List + +from cloudfoundry_client.v3.entities import EntityManager, ToManyRelationship, Entity, ToOneRelationship + +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + + +class RuleProtocol(Enum): + TCP = auto() + UDP = auto() + ICMP = auto() + ALL = auto() + + def __repr__(self): + return '%s' % self.name.lower() + + +@dataclass +class Rule: + protocol: RuleProtocol + destination: str + ports: Optional[str] = None + type: Optional[int] = None + code: Optional[int] = None + description: Optional[str] = None + log: Optional[bool] = None + + +@dataclass +class GloballyEnabled: + running: Optional[bool] = None + staging: Optional[bool] = None + + +class SecurityGroupManager(EntityManager): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(SecurityGroupManager, self).__init__(target_endpoint, client, "/v3/security_groups") + + def create(self, + name: str, + rules: Optional[List[Rule]] = None, + globally_enabled: Optional[GloballyEnabled] = None, + staging_spaces: Optional[ToManyRelationship] = None, + running_spaces: Optional[ToManyRelationship] = None) -> Entity: + payload = self._generate_payload(name, rules, globally_enabled, staging_spaces, running_spaces) + return super()._create(payload) + + def update(self, + security_group_id: str, + name: Optional[str] = None, + rules: Optional[List[Rule]] = None, + globally_enabled: Optional[GloballyEnabled] = None, + staging_spaces: Optional[ToManyRelationship] = None, + running_spaces: Optional[ToManyRelationship] = None) -> Entity: + payload = self._generate_payload(name, rules, globally_enabled, staging_spaces, running_spaces) + return super()._update(security_group_id, payload) + + def remove(self, security_group_id: str): + return super()._remove(security_group_id) + + def bind_running_security_group_to_spaces(self, security_group_id: str, space_guids: ToManyRelationship) \ + -> ToManyRelationship: + relationship = "running_spaces" + return self._bind_spaces(security_group_id, space_guids, relationship) + + def bind_staging_security_group_to_spaces(self, security_group_id: str, space_guids: ToManyRelationship) \ + -> ToManyRelationship: + relationship = "staging_spaces" + return self._bind_spaces(security_group_id, space_guids, relationship) + + def unbind_running_security_group_from_space(self, security_group_id: str, space_guid: ToOneRelationship): + relationship = "running_spaces" + return self._unbind_space(security_group_id, space_guid, relationship) + + def unbind_staging_security_group_from_space(self, security_group_id: str, space_guid: ToOneRelationship): + relationship = "staging_spaces" + return self._unbind_space(security_group_id, space_guid, relationship) + + def _bind_spaces(self, security_group_id: str, space_guids: ToManyRelationship, relationship: str) \ + -> ToManyRelationship: + url = "%s%s/%s/relationships/%s" % (self.target_endpoint, self.entity_uri, security_group_id, relationship) + return ToManyRelationship.from_json_object(super()._post(url, space_guids)) + + def _unbind_space(self, security_group_id: str, space_guid: ToOneRelationship, relationship: str): + url = "%s%s/%s/relationships/%s/%s" \ + % (self.target_endpoint, self.entity_uri, security_group_id, relationship, space_guid.guid) + super()._delete(url) + + @staticmethod + def _generate_payload(name: Optional[str], + rules: Optional[List[Rule]], + globally_enabled: Optional[GloballyEnabled], + staging_spaces: Optional[ToManyRelationship], + running_spaces: Optional[ToManyRelationship]): + payload = {} + if name: + payload["name"] = name + if rules: + payload["rules"] = [asdict(rule, dict_factory=lambda x: {k: repr(v) if k == "protocol" else v + for (k, v) in x if v is not None}) + for rule in rules] + if globally_enabled: + payload["globally_enabled"] = asdict(globally_enabled, + dict_factory=lambda x: {k: v for (k, v) in x if v is not None}) + relationships = dict() + if staging_spaces: + relationships["staging_spaces"] = staging_spaces + if running_spaces: + relationships["running_spaces"] = running_spaces + if len(relationships) > 0: + payload["relationships"] = relationships + return payload diff --git a/test/fixtures/v3/security_groups/GET_response.json b/test/fixtures/v3/security_groups/GET_response.json new file mode 100644 index 0000000..4318db5 --- /dev/null +++ b/test/fixtures/v3/security_groups/GET_response.json @@ -0,0 +1,84 @@ +{ + "pagination": { + "total_results": 1, + "total_pages": 1, + "first": { + "href": "https://api.example.org/v3/security_groups?page=1&per_page=50" + }, + "last": { + "href": "https://api.example.org/v3/security_groups?page=1&per_page=50" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "b85a788e-671f-4549-814d-e34cdb2f539a", + "created_at": "2020-02-20T17:42:08Z", + "updated_at": "2020-02-20T17:42:08Z", + "name": "my-group0", + "globally_enabled": { + "running": true, + "staging": false + }, + "rules": [ + { + "protocol": "tcp", + "destination": "10.10.10.0/24", + "ports": "443,80,8080" + }, + { + "protocol": "icmp", + "destination": "10.10.10.0/24", + "type": 8, + "code": 0, + "description": "Allow ping requests to private services" + } + ], + "relationships": { + "staging_spaces": { + "data": [ + { + "guid": "space-guid-1" + }, + { + "guid": "space-guid-2" + } + ] + }, + "running_spaces": { + "data": [] + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a" + } + } + }, + { + "guid": "a89a788e-671f-4549-814d-e34c1b2f533a", + "created_at": "2020-02-20T17:42:08Z", + "updated_at": "2020-02-20T17:42:08Z", + "name": "my-group1", + "globally_enabled": { + "running": true, + "staging": true + }, + "rules": [], + "relationships": { + "staging_spaces": { + "data": [] + }, + "running_spaces": { + "data": [] + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/security_groups/a89a788e-671f-4549-814d-e34c1b2f533a" + } + } + } + ] +} diff --git a/test/fixtures/v3/security_groups/GET_{id}_response.json b/test/fixtures/v3/security_groups/GET_{id}_response.json new file mode 100644 index 0000000..65c6e2a --- /dev/null +++ b/test/fixtures/v3/security_groups/GET_{id}_response.json @@ -0,0 +1,44 @@ +{ + "guid": "b85a788e-671f-4549-814d-e34cdb2f539a", + "created_at": "2020-02-20T17:42:08Z", + "updated_at": "2020-02-20T17:42:08Z", + "name": "my-group0", + "globally_enabled": { + "running": true, + "staging": false + }, + "rules": [ + { + "protocol": "tcp", + "destination": "10.10.10.0/24", + "ports": "443,80,8080" + }, + { + "protocol": "icmp", + "destination": "10.10.10.0/24", + "type": 8, + "code": 0, + "description": "Allow ping requests to private services" + } + ], + "relationships": { + "staging_spaces": { + "data": [ + { + "guid": "space-guid-1" + }, + { + "guid": "space-guid-2" + } + ] + }, + "running_spaces": { + "data": [] + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a" + } + } +} \ No newline at end of file diff --git a/test/fixtures/v3/security_groups/PATCH_{id}_response.json b/test/fixtures/v3/security_groups/PATCH_{id}_response.json new file mode 100644 index 0000000..65c6e2a --- /dev/null +++ b/test/fixtures/v3/security_groups/PATCH_{id}_response.json @@ -0,0 +1,44 @@ +{ + "guid": "b85a788e-671f-4549-814d-e34cdb2f539a", + "created_at": "2020-02-20T17:42:08Z", + "updated_at": "2020-02-20T17:42:08Z", + "name": "my-group0", + "globally_enabled": { + "running": true, + "staging": false + }, + "rules": [ + { + "protocol": "tcp", + "destination": "10.10.10.0/24", + "ports": "443,80,8080" + }, + { + "protocol": "icmp", + "destination": "10.10.10.0/24", + "type": 8, + "code": 0, + "description": "Allow ping requests to private services" + } + ], + "relationships": { + "staging_spaces": { + "data": [ + { + "guid": "space-guid-1" + }, + { + "guid": "space-guid-2" + } + ] + }, + "running_spaces": { + "data": [] + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a" + } + } +} \ No newline at end of file diff --git a/test/fixtures/v3/security_groups/POST_response.json b/test/fixtures/v3/security_groups/POST_response.json new file mode 100644 index 0000000..65c6e2a --- /dev/null +++ b/test/fixtures/v3/security_groups/POST_response.json @@ -0,0 +1,44 @@ +{ + "guid": "b85a788e-671f-4549-814d-e34cdb2f539a", + "created_at": "2020-02-20T17:42:08Z", + "updated_at": "2020-02-20T17:42:08Z", + "name": "my-group0", + "globally_enabled": { + "running": true, + "staging": false + }, + "rules": [ + { + "protocol": "tcp", + "destination": "10.10.10.0/24", + "ports": "443,80,8080" + }, + { + "protocol": "icmp", + "destination": "10.10.10.0/24", + "type": 8, + "code": 0, + "description": "Allow ping requests to private services" + } + ], + "relationships": { + "staging_spaces": { + "data": [ + { + "guid": "space-guid-1" + }, + { + "guid": "space-guid-2" + } + ] + }, + "running_spaces": { + "data": [] + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a" + } + } +} \ No newline at end of file diff --git a/test/fixtures/v3/security_groups/POST_{id}_relationships_running_spaces_response.json b/test/fixtures/v3/security_groups/POST_{id}_relationships_running_spaces_response.json new file mode 100644 index 0000000..a299de4 --- /dev/null +++ b/test/fixtures/v3/security_groups/POST_{id}_relationships_running_spaces_response.json @@ -0,0 +1,18 @@ +{ + "data": [ + { + "guid": "space-guid1" + }, + { + "guid": "space-guid2" + }, + { + "guid": "previous-space-guid" + } + ], + "links": { + "self": { + "href": "https://api.example.org/v3/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a/relationships/running_spaces" + } + } +} \ No newline at end of file diff --git a/test/fixtures/v3/security_groups/POST_{id}_relationships_staging_spaces_response.json b/test/fixtures/v3/security_groups/POST_{id}_relationships_staging_spaces_response.json new file mode 100644 index 0000000..2a5760a --- /dev/null +++ b/test/fixtures/v3/security_groups/POST_{id}_relationships_staging_spaces_response.json @@ -0,0 +1,18 @@ +{ + "data": [ + { + "guid": "space-guid1" + }, + { + "guid": "space-guid2" + }, + { + "guid": "previous-space-guid" + } + ], + "links": { + "self": { + "href": "https://api.example.org/v3/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a/relationships/staging_spaces" + } + } +} \ No newline at end of file diff --git a/test/v3/test_security_groups.py b/test/v3/test_security_groups.py new file mode 100644 index 0000000..885ae12 --- /dev/null +++ b/test/v3/test_security_groups.py @@ -0,0 +1,160 @@ +import unittest +from http import HTTPStatus + +from abstract_test_case import AbstractTestCase +from cloudfoundry_client.v3.entities import Entity, ToManyRelationship, ToOneRelationship +from cloudfoundry_client.v3.security_groups import Rule, RuleProtocol, GloballyEnabled + + +class TestSecurityGroups(unittest.TestCase, AbstractTestCase): + @classmethod + def setUpClass(cls): + cls.mock_client_class() + + def setUp(self): + self.build_client() + + def test_create(self): + self.client.post.return_value = self.mock_response( + "/v3/security_groups", HTTPStatus.CREATED, None, "v3", "security_groups", "POST_response.json" + ) + group_name = "my-group0" + result = self.client.v3.security_groups.create(group_name, rules=[ + Rule(protocol=RuleProtocol.TCP, destination="10.10.10.0/24", ports="443,80,8080"), + Rule(protocol=RuleProtocol.ICMP, destination="10.10.10.0/24", type=8, code=0, + description="Allow ping requests to private services")]) + self.client.post.assert_called_with( + self.client.post.return_value.url, + json={ + "name": group_name, + "rules": [ + {"protocol": "tcp", "destination": "10.10.10.0/24", "ports": "443,80,8080"}, + {"protocol": "icmp", "destination": "10.10.10.0/24", "type": 8, "code": 0, + "description": "Allow ping requests to private services"} + ] + }, + files=None + ) + self.assertIsNotNone(result) + self.assertIsInstance(result, Entity) + + def test_list(self): + self.client.get.return_value = self.mock_response( + "/v3/security_groups", HTTPStatus.OK, None, "v3", "security_groups", "GET_response.json" + ) + all_security_groups = [service_broker for service_broker in self.client.v3.security_groups.list()] + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual(2, len(all_security_groups)) + self.assertEqual(all_security_groups[0]["name"], "my-group0") + self.assertIsInstance(all_security_groups[0], Entity) + + def test_get(self): + self.client.get.return_value = self.mock_response( + "/v3/security_groups/security_group_guid_123", HTTPStatus.OK, None, "v3", "security_groups", + "GET_{id}_response.json" + ) + security_group = self.client.v3.security_groups.get("security_group_guid_123") + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual("my-group0", security_group["name"]) + self.assertIsInstance(security_group, Entity) + + def test_update(self): + self.client.patch.return_value = self.mock_response( + "/v3/security_groups/security_group_guid_123", HTTPStatus.OK, None, "v3", "security_groups", + "PATCH_{id}_response.json" + ) + group_name = "my-group0" + result = self.client.v3.security_groups.update("security_group_guid_123", + group_name, + rules=[ + Rule(protocol=RuleProtocol.TCP, destination="10.10.10.0/24", + ports="443,80,8080"), + Rule(protocol=RuleProtocol.ICMP, destination="10.10.10.0/24", + type=8, code=0, + description="Allow ping requests to private services") + ], + globally_enabled=GloballyEnabled(running=True)) + self.client.patch.assert_called_with( + self.client.patch.return_value.url, + json={ + "name": group_name, + "rules": [ + {"protocol": "tcp", "destination": "10.10.10.0/24", "ports": "443,80,8080"}, + {"protocol": "icmp", "destination": "10.10.10.0/24", "type": 8, "code": 0, + "description": "Allow ping requests to private services"} + ], + "globally_enabled": { + "running": True + } + } + ) + self.assertIsNotNone(result) + self.assertIsInstance(result, Entity) + + def test_remove(self): + self.client.delete.return_value = self.mock_response("/v3/security_groups/security_group_guid", + HTTPStatus.NO_CONTENT, None) + self.client.v3.security_groups.remove("security_group_guid") + self.client.delete.assert_called_with(self.client.delete.return_value.url) + + def test_bind_running_spaces(self): + self.client.post.return_value = self.mock_response( + "/v3/security_groups/security_group_guid/relationships/running_spaces", + HTTPStatus.OK, None, "v3", "security_groups", "POST_{id}_relationships_running_spaces_response.json" + ) + result = self.client.v3.security_groups.bind_running_security_group_to_spaces("security_group_guid", + ToManyRelationship("space-guid1", + "space-guid2")) + + self.client.post.assert_called_with( + self.client.post.return_value.url, + json={ + "data": [ + {"guid": "space-guid1"}, + {"guid": "space-guid2"} + ] + }, + files=None + ) + self.assertIsInstance(result, ToManyRelationship) + self.assertListEqual(["space-guid1", "space-guid2", "previous-space-guid"], result.guids) + + def test_bind_staging_spaces(self): + self.client.post.return_value = self.mock_response( + "/v3/security_groups/security_group_guid/relationships/staging_spaces", + HTTPStatus.OK, None, "v3", "security_groups", "POST_{id}_relationships_staging_spaces_response.json" + ) + result = self.client.v3.security_groups.bind_staging_security_group_to_spaces("security_group_guid", + ToManyRelationship("space-guid1", + "space-guid2")) + + self.client.post.assert_called_with( + self.client.post.return_value.url, + json={ + "data": [ + {"guid": "space-guid1"}, + {"guid": "space-guid2"} + ] + }, + files=None + ) + self.assertIsInstance(result, ToManyRelationship) + self.assertListEqual(["space-guid1", "space-guid2", "previous-space-guid"], result.guids) + + def test_unbind_running_from_space(self): + self.client.delete.return_value = self.mock_response( + "/v3/security_groups/security_group_guid/relationships/running_spaces/space-guid", + HTTPStatus.NO_CONTENT, None) + self.client.v3.security_groups.unbind_running_security_group_from_space("security_group_guid", + ToOneRelationship("space-guid")) + + self.client.delete.assert_called_with(self.client.delete.return_value.url) + + def test_unbind_staging_from_space(self): + self.client.delete.return_value = self.mock_response( + "/v3/security_groups/security_group_guid/relationships/staging_spaces/space-guid", + HTTPStatus.NO_CONTENT, None) + self.client.v3.security_groups.unbind_staging_security_group_from_space("security_group_guid", + ToOneRelationship("space-guid")) + + self.client.delete.assert_called_with(self.client.delete.return_value.url) From 7b0815b3877e1325eb934654ac1cef337ec8a8e0 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sat, 1 Jan 2022 16:36:01 +0100 Subject: [PATCH 121/264] Support python 3.10 Stop supporting python 3.6 as its is not supported anymore --- .github/workflows/pythonpackage.yml | 2 +- README.rst | 9 ++++++++- requirements.txt | 1 - setup.py | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index b51a9ae..9b668c5 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -9,7 +9,7 @@ jobs: strategy: max-parallel: 1 matrix: - python-version: [3.6, 3.7, 3.8, 3.9] + python-version: ["3.7", "3.8", "3.9", "3.10"] steps: - uses: actions/checkout@v1 diff --git a/README.rst b/README.rst index 6644346..fe00fbf 100644 --- a/README.rst +++ b/README.rst @@ -14,10 +14,17 @@ Installing Supported versions ~~~~~~~~~~~~~~~~~~ -:warning: Starting version ``1.11.0``, versions older that python ``3.6.0`` will not be supported anymore. This late version was released by the end 2016. +:warning: Starting version ``1.11.0``, versions older than python ``3.6.0`` will not be supported anymore. This late version was released by the end 2016. For those that are still using python 2.7, it won't be supported by the end of 2020 and all library shall stop supporting it. +:warning: Starting version ``1.25.0``, versions older than python ``3.7.0`` will not be supported anymore. + +See `official documentation`_. + +.. _`official documentation`: https://endoflife.date/python + + From pip ~~~~~~~~ diff --git a/requirements.txt b/requirements.txt index bcb1885..25af5e3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,3 @@ websocket-client==0.54.0 PyYAML==5.4.1 requests>=2.5.0 polling2==0.4.6 -dataclasses==0.8; python_version < '3.7' diff --git a/setup.py b/setup.py index 8eec4f1..938199b 100644 --- a/setup.py +++ b/setup.py @@ -72,8 +72,10 @@ def run(self): "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", "Topic :: Communications", ], entry_points={ From 5a57cf85e9b13275f49386ca065295ad8f5ce5bb Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sat, 1 Jan 2022 16:40:41 +0100 Subject: [PATCH 122/264] Prepare version 1.25.0 --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index f55ae21..0060018 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.24.0" +__version__ = "1.25.0" From 4015865d969d1c95ef764be388789d68914ab219 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sat, 1 Jan 2022 16:44:06 +0100 Subject: [PATCH 123/264] Fix readme --- README.rst | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index fe00fbf..16159cb 100644 --- a/README.rst +++ b/README.rst @@ -14,11 +14,9 @@ Installing Supported versions ~~~~~~~~~~~~~~~~~~ -:warning: Starting version ``1.11.0``, versions older than python ``3.6.0`` will not be supported anymore. This late version was released by the end 2016. - -For those that are still using python 2.7, it won't be supported by the end of 2020 and all library shall stop supporting it. - -:warning: Starting version ``1.25.0``, versions older than python ``3.7.0`` will not be supported anymore. +- Starting version ``1.11.0``, versions older than python ``3.6.0`` will not be supported anymore. This late version was released by the end 2016. + For those that are still using python 2.7, it won't be supported by the end of 2020 and all library shall stop supporting it. +- Starting version ``1.25.0``, versions older than python ``3.7.0`` will not be supported anymore. See `official documentation`_. From 313cac7d61638405aa071d4249639252f5978dbf Mon Sep 17 00:00:00 2001 From: Will Gant Date: Wed, 5 Jan 2022 11:18:20 +0000 Subject: [PATCH 124/264] Bump pyyaml to 6.0 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d7fa2ca..6754860 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,6 +2,6 @@ aiohttp==3.7.4 protobuf>=3.6.1 oauth2-client==1.2.1 websocket-client==0.54.0 -PyYAML==5.4.1 +PyYAML==6.0 requests>=2.5.0 polling2==0.4.6 From f7738ef48b9627bf8313d85b8f75b7cc4146463c Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 4 Jan 2022 16:38:20 -0500 Subject: [PATCH 125/264] implement Roles in v3 API --- README.rst | 1 + main/cloudfoundry_client/client.py | 2 + main/cloudfoundry_client/v3/roles.py | 14 ++++ test/fixtures/v3/roles/GET_response.json | 82 +++++++++++++++++++ test/fixtures/v3/roles/GET_{id}_response.json | 32 ++++++++ test/v3/test_roles.py | 37 +++++++++ 6 files changed, 168 insertions(+) create mode 100644 main/cloudfoundry_client/v3/roles.py create mode 100644 test/fixtures/v3/roles/GET_response.json create mode 100644 test/fixtures/v3/roles/GET_{id}_response.json create mode 100644 test/v3/test_roles.py diff --git a/README.rst b/README.rst index 16159cb..01b9879 100644 --- a/README.rst +++ b/README.rst @@ -249,6 +249,7 @@ Available managers on API V3 are: - ``organizations`` - ``organization_quotas`` - ``processes`` +- ``roles`` - ``security_groups`` - ``service_brokers`` - ``service_credential_bindings`` diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index ef3a3ab..6566a18 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -32,6 +32,7 @@ from cloudfoundry_client.v3.organization_quotas import OrganizationQuotaManager from cloudfoundry_client.v3.processes import ProcessManager from cloudfoundry_client.v3.organizations import OrganizationManager +from cloudfoundry_client.v3.roles import RoleManager from cloudfoundry_client.v3.security_groups import SecurityGroupManager from cloudfoundry_client.v3.service_brokers import ServiceBrokerManager from cloudfoundry_client.v3.service_credential_bindings import ServiceCredentialBindingManager @@ -106,6 +107,7 @@ def __init__(self, target_endpoint: str, credential_manager: "CloudFoundryClient self.organizations = OrganizationManager(target_endpoint, credential_manager) self.organization_quotas = OrganizationQuotaManager(target_endpoint, credential_manager) self.processes = ProcessManager(target_endpoint, credential_manager) + self.roles = RoleManager(target_endpoint, credential_manager) self.security_groups = SecurityGroupManager(target_endpoint, credential_manager) self.service_brokers = ServiceBrokerManager(target_endpoint, credential_manager) self.service_credential_bindings = ServiceCredentialBindingManager(target_endpoint, credential_manager) diff --git a/main/cloudfoundry_client/v3/roles.py b/main/cloudfoundry_client/v3/roles.py new file mode 100644 index 0000000..06830ee --- /dev/null +++ b/main/cloudfoundry_client/v3/roles.py @@ -0,0 +1,14 @@ +from typing import TYPE_CHECKING + +from cloudfoundry_client.v3.entities import EntityManager + +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + + +class RoleManager(EntityManager): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(RoleManager, self).__init__(target_endpoint, client, "/v3/roles") + + def remove(self, role_guid: str): + super(RoleManager, self)._remove(role_guid) diff --git a/test/fixtures/v3/roles/GET_response.json b/test/fixtures/v3/roles/GET_response.json new file mode 100644 index 0000000..af32af0 --- /dev/null +++ b/test/fixtures/v3/roles/GET_response.json @@ -0,0 +1,82 @@ +{ + "pagination": { + "total_results": 3, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/roles?page=1&per_page=2" + }, + "last": { + "href": "https://api.example.org/v3/roles?page=2&per_page=2" + }, + "next": { + "href": null + }, + "previous": null + }, + "resources": [ + { + "guid": "40557c70-d1bd-4976-a2ab-a85f5e882418", + "created_at": "2019-10-10T17:19:12Z", + "updated_at": "2019-10-10T17:19:12Z", + "type": "organization_auditor", + "relationships": { + "user": { + "data": { + "guid": "59eadb5f-fc13-414f-84ba-77a35e239cc8" + } + }, + "organization": { + "data": { + "guid": "05c5da3b-6cbc-421c-87c3-20bb3c41ab7c" + } + }, + "space": { + "data": null + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/roles/40557c70-d1bd-4976-a2ab-a85f5e882418" + }, + "user": { + "href": "https://api.example.org/v3/users/59eadb5f-fc13-414f-84ba-77a35e239cc8" + }, + "organization": { + "href": "https://api.example.org/v3/organizations/05c5da3b-6cbc-421c-87c3-20bb3c41ab7c" + } + } + }, + { + "guid": "12347c70-d1bd-4976-a2ab-a85f5e882418", + "created_at": "2047-11-10T17:19:12Z", + "updated_at": "2047-11-10T17:19:12Z", + "type": "space_auditor", + "relationships": { + "user": { + "data": { + "guid": "47eadb5f-fc13-414f-84ba-47a35e239cc8" + } + }, + "organization": { + "data": null + }, + "space": { + "data": { + "guid": "47c5da3b-6cbc-421c-87c3-20bb3c41ab7c" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/roles/12347c70-d1bd-4976-a2ab-a85f5e882418" + }, + "user": { + "href": "https://api.example.org/v3/users/47eadb5f-fc13-414f-84ba-77a35e239cc8" + }, + "space": { + "href": "https://api.example.org/v3/spaces/47c5da3b-6cbc-421c-87c3-20bb3c41ab7c" + } + } + } + ] + } \ No newline at end of file diff --git a/test/fixtures/v3/roles/GET_{id}_response.json b/test/fixtures/v3/roles/GET_{id}_response.json new file mode 100644 index 0000000..f8e9ede --- /dev/null +++ b/test/fixtures/v3/roles/GET_{id}_response.json @@ -0,0 +1,32 @@ +{ + "guid": "role_id", + "created_at": "2019-10-10T17:19:12Z", + "updated_at": "2019-10-10T17:19:12Z", + "type": "organization_auditor", + "relationships": { + "user": { + "data": { + "guid": "59eadb5f-fc13-414f-84ba-77a35e239cc8" + } + }, + "organization": { + "data": { + "guid": "05c5da3b-6cbc-421c-87c3-20bb3c41ab7c" + } + }, + "space": { + "data": null + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/roles/40557c70-d1bd-4976-a2ab-a85f5e882418" + }, + "user": { + "href": "https://api.example.org/v3/users/59eadb5f-fc13-414f-84ba-77a35e239cc8" + }, + "organization": { + "href": "https://api.example.org/v3/organizations/05c5da3b-6cbc-421c-87c3-20bb3c41ab7c" + } + } + } \ No newline at end of file diff --git a/test/v3/test_roles.py b/test/v3/test_roles.py new file mode 100644 index 0000000..4be2363 --- /dev/null +++ b/test/v3/test_roles.py @@ -0,0 +1,37 @@ +import unittest +from http import HTTPStatus + +from abstract_test_case import AbstractTestCase +from cloudfoundry_client.v3.entities import Entity + + +class TestRoles(unittest.TestCase, AbstractTestCase): + @classmethod + def setUpClass(cls): + cls.mock_client_class() + + def setUp(self): + self.build_client() + + def test_list(self): + self.client.get.return_value = self.mock_response("/v3/roles", HTTPStatus.OK, None, "v3", "roles", + "GET_response.json") + all_roles = [role for role in self.client.v3.roles.list()] + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual(2, len(all_roles)) + self.assertEqual(all_roles[0]["type"], "organization_auditor") + self.assertIsInstance(all_roles[0], Entity) + + def test_get(self): + self.client.get.return_value = self.mock_response( + "/v3/roles/role_id", HTTPStatus.OK, None, "v3", "roles", "GET_{id}_response.json" + ) + role = self.client.v3.roles.get("role_id") + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual(role["type"], "organization_auditor") + self.assertIsInstance(role, Entity) + + def test_remove(self): + self.client.delete.return_value = self.mock_response("/v3/roles/role_id", HTTPStatus.NO_CONTENT, None) + self.client.v3.roles.remove("role_id") + self.client.delete.assert_called_with(self.client.delete.return_value.url) From 81c47d4e69632937fb4668f7abac210d0fa93f43 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Wed, 5 Jan 2022 20:43:56 +0100 Subject: [PATCH 126/264] Prepare version 1.26.0 --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 0060018..68f3e1d 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.25.0" +__version__ = "1.26.0" From 2491909fa16cb0f4b8b2aa7f9ba372ef1465028b Mon Sep 17 00:00:00 2001 From: Cai Cooper <2406847+cagiti@users.noreply.github.com> Date: Mon, 10 Jan 2022 17:33:03 +0000 Subject: [PATCH 127/264] Bump websocket-client to 0.59.0 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index dd531fd..a5b96e8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ aiohttp>=3.8.0 protobuf>=3.6.1 oauth2-client==1.2.1 -websocket-client==0.54.0 +websocket-client==0.59.0 PyYAML==6.0 requests>=2.5.0 polling2==0.4.6 From d0fdbeffe5e2f127c86e756d5f71e54c3cf5ad41 Mon Sep 17 00:00:00 2001 From: Cai Cooper <2406847+cagiti@users.noreply.github.com> Date: Mon, 10 Jan 2022 17:39:30 +0000 Subject: [PATCH 128/264] Bumping polling to 0.5.0 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index dd531fd..b3698a1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,4 @@ oauth2-client==1.2.1 websocket-client==0.54.0 PyYAML==6.0 requests>=2.5.0 -polling2==0.4.6 +polling2==0.5.0 From e626a4618ca171f250eae10c4a50de777f735591 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Tue, 11 Jan 2022 09:57:49 +0100 Subject: [PATCH 129/264] Prepare version 1.27.0 --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 68f3e1d..18bb462 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.26.0" +__version__ = "1.27.0" From c1d570063ba8aeb8a7923502fe3e8ccc28131427 Mon Sep 17 00:00:00 2001 From: Subhash Chandran Date: Sat, 19 Feb 2022 00:33:12 +0530 Subject: [PATCH 130/264] option to choose a range on timestamp field for /v2/events --- main/cloudfoundry_client/v2/entities.py | 9 +++++++++ test/v2/test_entities.py | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/main/cloudfoundry_client/v2/entities.py b/main/cloudfoundry_client/v2/entities.py index fbc7af7..1c45875 100644 --- a/main/cloudfoundry_client/v2/entities.py +++ b/main/cloudfoundry_client/v2/entities.py @@ -50,6 +50,8 @@ class EntityManager(object): list_multi_parameters = ["order-by"] + timestamp_parameters = ["timestamp"] + def __init__( self, target_endpoint: str, client: "CloudFoundryClient", entity_uri: str, entity_builder: Optional[EntityBuilder] = None ): @@ -150,6 +152,13 @@ def _append_encoded_parameter(parameters: List[str], args: Tuple[str, Any]) -> L value_list = [value_list] for value in value_list: parameters.append("%s=%s" % (parameter_name, str(value))) + elif parameter_name in self.timestamp_parameters: + if isinstance(args[1], dict): + operator_list = args[1].keys() + for operator in operator_list: + parameters.append("q=%s" % quote("%s%s%s" % (parameter_name, operator, args[1][operator]))) + else: + parameters.append("q=%s" % quote("%s:%s" % (parameter_name, str(parameter_value)))) elif isinstance(parameter_value, (list, tuple)): parameters.append("q=%s" % quote("%s IN %s" % (parameter_name, ",".join(parameter_value)))) else: diff --git a/test/v2/test_entities.py b/test/v2/test_entities.py index d08c2a6..93ca3d6 100644 --- a/test/v2/test_entities.py +++ b/test/v2/test_entities.py @@ -65,6 +65,12 @@ def test_multi_query(self): ) self.assertEqual("/v2/events?q=organization_guid%3Aorg-id&q=type%20IN%20create%2Cupdate", url) + def test_range_query(self): + url = EntityManager("http://cf.api", None, "/v2/events")._get_url_filtered( + "/v2/events", **{"type": "app.crash", "space_guid": "space-id", "timestamp": {">": "2022-02-08T16:41:25Z", "<": "2022-02-10T16:41:25Z"}} + ) + self.assertEqual("/v2/events?q=space_guid%3Aspace-id&q=timestamp%3E2022-02-08T16%3A41%3A25Z&q=timestamp%3C2022-02-10T16%3A41%3A25Z&q=type%3Aapp.crash", url) + def test_list(self): client = MagicMock() entity_manager = EntityManager(self.TARGET_ENDPOINT, client, "/fake/first") From fb1dd7406cc7d94dac769a00f8f69058ca5abe54 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sat, 19 Feb 2022 09:27:39 +0100 Subject: [PATCH 131/264] Propagate asynchronous parameter where delete is asynchronous --- main/cloudfoundry_client/v3/apps.py | 6 +++--- main/cloudfoundry_client/v3/buildpacks.py | 4 ++-- main/cloudfoundry_client/v3/domains.py | 4 ++-- main/cloudfoundry_client/v3/entities.py | 20 +++++++++++-------- .../v3/organization_quotas.py | 4 ++-- main/cloudfoundry_client/v3/organizations.py | 4 ++-- main/cloudfoundry_client/v3/roles.py | 6 +++--- .../cloudfoundry_client/v3/security_groups.py | 4 ++-- .../cloudfoundry_client/v3/service_brokers.py | 4 ++-- 9 files changed, 30 insertions(+), 26 deletions(-) diff --git a/main/cloudfoundry_client/v3/apps.py b/main/cloudfoundry_client/v3/apps.py index 2a11b56..272dff4 100644 --- a/main/cloudfoundry_client/v3/apps.py +++ b/main/cloudfoundry_client/v3/apps.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional from cloudfoundry_client.json_object import JsonObject from cloudfoundry_client.v3.entities import EntityManager @@ -16,8 +16,8 @@ def restart(self, application_guid: str): self.entity_uri, application_guid)) - def remove(self, application_guid: str): - super(AppManager, self)._remove(application_guid) + def remove(self, application_guid: str, asynchronous: bool = True) -> Optional[str]: + return super(AppManager, self)._remove(application_guid, asynchronous) def get_env(self, application_guid: str) -> JsonObject: return super(AppManager, self)._get("%s%s/%s/env" % (self.target_endpoint, self.entity_uri, application_guid)) diff --git a/main/cloudfoundry_client/v3/buildpacks.py b/main/cloudfoundry_client/v3/buildpacks.py index 6405425..f6a1284 100644 --- a/main/cloudfoundry_client/v3/buildpacks.py +++ b/main/cloudfoundry_client/v3/buildpacks.py @@ -30,8 +30,8 @@ def create( } return super(BuildpackManager, self)._create(data) - def remove(self, buildpack_guid: str): - super(BuildpackManager, self)._remove(buildpack_guid) + def remove(self, buildpack_guid: str, asynchronous: bool = True) -> Optional[str]: + return super(BuildpackManager, self)._remove(buildpack_guid, asynchronous) def update( self, diff --git a/main/cloudfoundry_client/v3/domains.py b/main/cloudfoundry_client/v3/domains.py index 0702290..2ffe42c 100644 --- a/main/cloudfoundry_client/v3/domains.py +++ b/main/cloudfoundry_client/v3/domains.py @@ -50,8 +50,8 @@ def update(self, domain_guid: str, meta_labels: Optional[dict] = None, meta_anno data = {"metadata": {"labels": meta_labels, "annotations": meta_annotations}} return super(DomainManager, self)._update(domain_guid, data) - def remove(self, domain_guid: str): - super(DomainManager, self)._remove(domain_guid) + def remove(self, domain_guid: str, asynchronous: bool = True) -> Optional[str]: + return super(DomainManager, self)._remove(domain_guid, asynchronous) def __create_shared_domain_url(self, domain_guid: str) -> str: # TODO use url parser for this diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index 2b26672..5a8735c 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -148,6 +148,18 @@ def _delete(self, url: str) -> Optional[str]: except (AttributeError, KeyError): return None + def _remove(self, resource_id: str, asynchronous: bool = True) -> Optional[str]: + url = "%s%s/%s" % (self.target_endpoint, self.entity_uri, resource_id) + job_location = self._delete(url) + if job_location is not None: + job_url = urlparse(job_location) + job_guid = job_url.path.rsplit("/", 1)[-1] + if not asynchronous: + self.client.v3.jobs.wait_for_job_completion(job_guid) + else: + return job_guid + return None + def _list(self, requested_path: str, entity_type: Optional[ENTITY_TYPE] = None, **kwargs) -> PaginateEntities: url_requested = EntityManager._get_url_with_encoded_params("%s%s" % (self.target_endpoint, requested_path), **kwargs) for element in self._paginate(url_requested, entity_type): @@ -194,14 +206,6 @@ def _update(self, resource_id: str, data: dict) -> Entity: url = "%s%s/%s" % (self.target_endpoint, self.entity_uri, resource_id) return self._patch(url, data) - def _remove(self, resource_id: str, asynchronous: bool = True): - url = "%s%s/%s" % (self.target_endpoint, self.entity_uri, resource_id) - job_location = self._delete(url) - if not asynchronous and job_location is not None: - job_url = urlparse(job_location) - job_guid = job_url.path.rsplit("/", 1)[-1] - self.client.v3.jobs.wait_for_job_completion(job_guid) - def __iter__(self) -> PaginateEntities: return self.list() diff --git a/main/cloudfoundry_client/v3/organization_quotas.py b/main/cloudfoundry_client/v3/organization_quotas.py index 23a6bb7..abe5015 100644 --- a/main/cloudfoundry_client/v3/organization_quotas.py +++ b/main/cloudfoundry_client/v3/organization_quotas.py @@ -37,8 +37,8 @@ class OrganizationQuotaManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super().__init__(target_endpoint, client, "/v3/organization_quotas") - def remove(self, guid: str): - super()._remove(guid) + def remove(self, guid: str, asynchronous: bool = True) -> Optional[str]: + return super()._remove(guid, asynchronous) def create( self, diff --git a/main/cloudfoundry_client/v3/organizations.py b/main/cloudfoundry_client/v3/organizations.py index 601cd83..2086d31 100644 --- a/main/cloudfoundry_client/v3/organizations.py +++ b/main/cloudfoundry_client/v3/organizations.py @@ -27,8 +27,8 @@ def update( data = {"name": name, "suspended": suspended, "metadata": {"labels": meta_labels, "annotations": meta_annotations}} return super(OrganizationManager, self)._update(guid, data) - def remove(self, guid: str): - super(OrganizationManager, self)._remove(guid) + def remove(self, guid: str, asynchronous: bool = True) -> Optional[str]: + return super(OrganizationManager, self)._remove(guid, asynchronous) def assign_default_isolation_segment(self, org_guid: str, iso_seg_guid: str) -> Entity: return ToOneRelationship.from_json_object( diff --git a/main/cloudfoundry_client/v3/roles.py b/main/cloudfoundry_client/v3/roles.py index 06830ee..9123d5a 100644 --- a/main/cloudfoundry_client/v3/roles.py +++ b/main/cloudfoundry_client/v3/roles.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional from cloudfoundry_client.v3.entities import EntityManager @@ -10,5 +10,5 @@ class RoleManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(RoleManager, self).__init__(target_endpoint, client, "/v3/roles") - def remove(self, role_guid: str): - super(RoleManager, self)._remove(role_guid) + def remove(self, role_guid: str, asynchronous: bool = True) -> Optional[str]: + return super(RoleManager, self)._remove(role_guid, asynchronous) diff --git a/main/cloudfoundry_client/v3/security_groups.py b/main/cloudfoundry_client/v3/security_groups.py index d539474..b134e03 100644 --- a/main/cloudfoundry_client/v3/security_groups.py +++ b/main/cloudfoundry_client/v3/security_groups.py @@ -58,8 +58,8 @@ def update(self, payload = self._generate_payload(name, rules, globally_enabled, staging_spaces, running_spaces) return super()._update(security_group_id, payload) - def remove(self, security_group_id: str): - return super()._remove(security_group_id) + def remove(self, security_group_id: str, asynchronous: bool = True) -> Optional[str]: + return super()._remove(security_group_id, asynchronous) def bind_running_security_group_to_spaces(self, security_group_id: str, space_guids: ToManyRelationship) \ -> ToManyRelationship: diff --git a/main/cloudfoundry_client/v3/service_brokers.py b/main/cloudfoundry_client/v3/service_brokers.py index 4c15886..c39795b 100644 --- a/main/cloudfoundry_client/v3/service_brokers.py +++ b/main/cloudfoundry_client/v3/service_brokers.py @@ -59,5 +59,5 @@ def update( payload["metadata"] = metadata return super(ServiceBrokerManager, self)._update(guid, payload) - def remove(self, guid: str): - super(ServiceBrokerManager, self)._remove(guid) + def remove(self, guid: str, asynchronous: bool = True) -> Optional[str]: + return super(ServiceBrokerManager, self)._remove(guid, asynchronous) From 11a8bebc6ec9bed817695cf4a4c58e640c4853a7 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sat, 19 Feb 2022 09:56:21 +0100 Subject: [PATCH 132/264] [service credential bindings] Implement create operation Close #164 --- main/cloudfoundry_client/v3/entities.py | 13 +++- .../v3/service_credential_bindings.py | 43 ++++++++++++- .../POST_response.json | 50 +++++++++++++++ test/v3/test_service_credential_bindings.py | 61 +++++++++++++++++++ 4 files changed, 163 insertions(+), 4 deletions(-) create mode 100644 test/fixtures/v3/service_credential_bindings/POST_response.json diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index 5a8735c..615a881 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -143,6 +143,10 @@ def _patch(self, url: str, data: dict, entity_type: Optional[ENTITY_TYPE] = None def _delete(self, url: str) -> Optional[str]: response = self.client.delete(url) + return self._location(response) + + @staticmethod + def _location(response): try: return response.headers["Location"] except (AttributeError, KeyError): @@ -152,14 +156,19 @@ def _remove(self, resource_id: str, asynchronous: bool = True) -> Optional[str]: url = "%s%s/%s" % (self.target_endpoint, self.entity_uri, resource_id) job_location = self._delete(url) if job_location is not None: - job_url = urlparse(job_location) - job_guid = job_url.path.rsplit("/", 1)[-1] + job_guid = self._extract_job_guid(job_location) if not asynchronous: self.client.v3.jobs.wait_for_job_completion(job_guid) else: return job_guid return None + @staticmethod + def _extract_job_guid(job_location): + job_url = urlparse(job_location) + job_guid = job_url.path.rsplit("/", 1)[-1] + return job_guid + def _list(self, requested_path: str, entity_type: Optional[ENTITY_TYPE] = None, **kwargs) -> PaginateEntities: url_requested = EntityManager._get_url_with_encoded_params("%s%s" % (self.target_endpoint, requested_path), **kwargs) for element in self._paginate(url_requested, entity_type): diff --git a/main/cloudfoundry_client/v3/service_credential_bindings.py b/main/cloudfoundry_client/v3/service_credential_bindings.py index ad343f5..d587b7a 100644 --- a/main/cloudfoundry_client/v3/service_credential_bindings.py +++ b/main/cloudfoundry_client/v3/service_credential_bindings.py @@ -1,6 +1,6 @@ -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional, Union -from cloudfoundry_client.v3.entities import EntityManager +from cloudfoundry_client.v3.entities import EntityManager, Entity, ToOneRelationship if TYPE_CHECKING: from cloudfoundry_client.client import CloudFoundryClient @@ -10,3 +10,42 @@ class ServiceCredentialBindingManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(ServiceCredentialBindingManager, self).__init__(target_endpoint, client, "/v3/service_credential_bindings") + + def create( + self, + name: str, + service_credential_binding_type: str, + service_instance_guid: str, + application_guid: Optional[str], + parameters: Optional[dict], + meta_labels: Optional[dict], + meta_annotations: Optional[dict], + asynchronous: bool = True, + ) -> Union[str, Entity, None]: + data = { + "name": name, + "type": service_credential_binding_type, + "relationships": {"service_instance": ToOneRelationship(service_instance_guid)}, + } + if application_guid: + data["relationships"]["app"] = ToOneRelationship(application_guid) + if parameters: + data["parameters"] = parameters + if meta_labels or meta_annotations: + metadata = dict() + if meta_labels: + metadata["labels"] = meta_labels + if meta_annotations: + metadata["annotations"] = meta_annotations + data["metadata"] = metadata + url = "%s%s" % (self.target_endpoint, self.entity_uri) + response = self.client.post(url, json=data) + location = super(ServiceCredentialBindingManager, self)._location(response) + if location: + job_guid = super(ServiceCredentialBindingManager, self)._extract_job_guid(location) + if asynchronous: + return job_guid + else: + self.client.v3.jobs.wait_for_job_completion(job_guid) + return None + return super(ServiceCredentialBindingManager, self)._read_response(response, None) diff --git a/test/fixtures/v3/service_credential_bindings/POST_response.json b/test/fixtures/v3/service_credential_bindings/POST_response.json new file mode 100644 index 0000000..d8b2089 --- /dev/null +++ b/test/fixtures/v3/service_credential_bindings/POST_response.json @@ -0,0 +1,50 @@ +{ + "guid": "dde5ad2a-d8f4-44dc-a56f-0452d744f1c3", + "created_at": "2015-11-13T17:02:56Z", + "updated_at": "2016-06-08T16:41:26Z", + "name": "some-name", + "type": "app", + "last_operation": { + "type": "create", + "state": "succeeded", + "created_at": "2015-11-13T17:02:56Z", + "updated_at": "2016-06-08T16:41:26Z" + }, + "metadata": { + "annotations": { + "foo": "bar" + }, + "labels": { + "baz": "qux" + } + }, + "relationships": { + "app": { + "data": { + "guid": "74f7c078-0934-470f-9883-4fddss5b8f13" + } + }, + "service_instance": { + "data": { + "guid": "8bfe4c1b-9e18-45b1-83be-124163f31f9e" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/service_credential_bindings/dde5ad2a-d8f4-44dc-a56f-0452d744f1c3" + }, + "details": { + "href": "https://api.example.org/v3/service_credential_bindings/dde5ad2a-d8f4-44dc-a56f-0452d744f1c3/details" + }, + "parameters": { + "href": "https://api.example.org/v3/service_credential_bindings/dde5ad2a-d8f4-44dc-a56f-0452d744f1c3/parameters" + }, + "service_instance": { + "href": "https://api.example.org/v3/service_instances/8bfe4c1b-9e18-45b1-83be-124163f31f9e" + }, + "app": { + "href": "https://api.example.org/v3/apps/74f7c078-0934-470f-9883-4fddss5b8f13" + } + } +} diff --git a/test/v3/test_service_credential_bindings.py b/test/v3/test_service_credential_bindings.py index 6ec8bfc..945010f 100644 --- a/test/v3/test_service_credential_bindings.py +++ b/test/v3/test_service_credential_bindings.py @@ -13,6 +13,67 @@ def setUpClass(cls): def setUp(self): self.build_client() + def test_create_managed_service_instance(self): + self.client.post.return_value = self.mock_response( + "/v3/service_credential_bindings", HTTPStatus.ACCEPTED, + dict(Location="https://api.example.org/v3/jobs/af5c57f6-8769-41fa-a499-2c84ed896788") + ) + location = self.client.v3.service_credential_bindings.create("some-binding-name", "app", + "7304bc3c-7010-11ea-8840-48bf6bec2d78", + "e0e4417c-74ee-11ea-a604-48bf6bec2d78", + parameters=dict(key1="value1", key2="value2"), + meta_labels=dict(foo="bar"), + meta_annotations=dict(baz="qux")) + + self.assertEqual("af5c57f6-8769-41fa-a499-2c84ed896788", location) + self.client.post.assert_called_with( + self.client.post.return_value.url, + json={ + "type": "app", + "name": "some-binding-name", + "relationships": { + "service_instance": { + "data": {"guid": "7304bc3c-7010-11ea-8840-48bf6bec2d78"} + }, + "app": { + "data": {"guid": "e0e4417c-74ee-11ea-a604-48bf6bec2d78"} + } + }, + "parameters": {"key1": "value1", "key2": "value2"}, + "metadata": {"labels": {"foo": "bar"}, "annotations": {"baz": "qux"}}, + }, + ) + + def test_create_user_provided_service_instance(self): + self.client.post.return_value = self.mock_response( + "/v3/service_credential_bindings", HTTPStatus.ACCEPTED, + None, + "v3", "service_credential_bindings", "POST_response.json" + ) + result = self.client.v3.service_credential_bindings.create("some-binding-name", "key", + "7304bc3c-7010-11ea-8840-48bf6bec2d78", + None, + parameters=dict(key1="value1", key2="value2"), + meta_labels=dict(foo="bar"), + meta_annotations=dict(baz="qux")) + self.assertIsNotNone(result) + self.assertEqual("some-name", result["name"]) + self.assertIsInstance(result, Entity) + self.client.post.assert_called_with( + self.client.post.return_value.url, + json={ + "type": "key", + "name": "some-binding-name", + "relationships": { + "service_instance": { + "data": {"guid": "7304bc3c-7010-11ea-8840-48bf6bec2d78"} + } + }, + "parameters": {"key1": "value1", "key2": "value2"}, + "metadata": {"labels": {"foo": "bar"}, "annotations": {"baz": "qux"}}, + }, + ) + def test_list(self): self.client.get.return_value = self.mock_response( "/v3/service_credential_bindings", HTTPStatus.OK, None, "v3", "service_credential_bindings", From cc755acd10293b5281f10f6cdaf087fa3286154b Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sat, 19 Feb 2022 10:08:06 +0100 Subject: [PATCH 133/264] Prepare version 1.28.0 --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 18bb462..c40cff4 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.27.0" +__version__ = "1.28.0" From c0ecbb8ec85040fc2f74b6c52e1f9a6c6c16c4b0 Mon Sep 17 00:00:00 2001 From: Subhash Chandran Date: Sun, 20 Feb 2022 21:37:14 +0530 Subject: [PATCH 134/264] fix line length linting: removed less-than option from test-case --- test/v2/test_entities.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/v2/test_entities.py b/test/v2/test_entities.py index 93ca3d6..9f7d352 100644 --- a/test/v2/test_entities.py +++ b/test/v2/test_entities.py @@ -67,9 +67,9 @@ def test_multi_query(self): def test_range_query(self): url = EntityManager("http://cf.api", None, "/v2/events")._get_url_filtered( - "/v2/events", **{"type": "app.crash", "space_guid": "space-id", "timestamp": {">": "2022-02-08T16:41:25Z", "<": "2022-02-10T16:41:25Z"}} + "/v2/events", **{"type": "app.crash", "space_guid": "space-id", "timestamp": {">": "2022-02-08T16:41:25Z"}} ) - self.assertEqual("/v2/events?q=space_guid%3Aspace-id&q=timestamp%3E2022-02-08T16%3A41%3A25Z&q=timestamp%3C2022-02-10T16%3A41%3A25Z&q=type%3Aapp.crash", url) + self.assertEqual("/v2/events?q=space_guid%3Aspace-id&q=timestamp%3E2022-02-08T16%3A41%3A25Z&q=type%3Aapp.crash", url) def test_list(self): client = MagicMock() From 00dc49a4e00bd932e3a6fa27fee24c61c591946b Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sun, 20 Feb 2022 17:27:42 +0100 Subject: [PATCH 135/264] Prepare version 1.30.0 --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index c40cff4..b300eef 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.28.0" +__version__ = "1.30.0" From 488e5702d4f919228b993f6fb9e921e6e1e9bf4e Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Thu, 7 Jul 2022 17:14:48 +0200 Subject: [PATCH 136/264] [fixes] Requirements --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7dd3553..7ce708e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ aiohttp>=3.8.0 -protobuf>=3.6.1 +protobuf>=3.6.1, < 3.19.0 oauth2-client==1.2.1 websocket-client==0.59.0 PyYAML==6.0 From 6186be6cb9abf34dded0f9362c1a8689ece767d5 Mon Sep 17 00:00:00 2001 From: Sven Hergenhahn Date: Fri, 8 Jul 2022 18:25:14 +0200 Subject: [PATCH 137/264] Add build_from_cf_config() to use cf login credentials (#175) * Add build_from_cf_config() to allow using credentials from a previous cf login Authored-by: Sven Hergenhahn See #172 --- README.rst | 13 +++++++++++++ main/cloudfoundry_client/client.py | 19 +++++++++++++++++-- test/test_client.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 01b9879..1865fd1 100644 --- a/README.rst +++ b/README.rst @@ -73,6 +73,19 @@ To instantiate the client, nothing easier client.refresh_token = 'refresh-token' client._access_token = 'access-token' +You can also instantiate the client by reading the config file generated by `cf login`, which allows for authenticating via SSO and LDAP: + +.. code-block:: python + + # init with endpoint & token from the cf cli config file + from cloudfoundry_client.client import CloudFoundryClient + + # use the default file, i.e. ~/.cf/config.json + client = CloudFoundryClient.build_from_cf_config() + # or specify an alternative path + # - other kwargs can be passed through to CloudFoundryClient instantiation + client = CloudFoundryClient.build_from_cf_config(config_path="some/path/config.json", proxy=proxy, verify=False) + It can also be instantiated with oauth code flow if you possess a dedicated oauth application with its redirection .. code-block:: python diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 6566a18..a49230a 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -1,4 +1,6 @@ import logging +from pathlib import Path +import json from http import HTTPStatus from typing import Optional @@ -120,8 +122,8 @@ def __init__(self, target_endpoint: str, credential_manager: "CloudFoundryClient class CloudFoundryClient(CredentialManager): def __init__(self, target_endpoint: str, client_id: str = "cf", client_secret: str = "", **kwargs): - """ " - :param target_endpoint :the target endpoint + """ + :param target_endpoint :the target endpoint. :param client_id: the client_id :param client_secret: the client secret :param proxy: a dict object with entries http and https @@ -206,6 +208,19 @@ def _get_info(self, target_endpoint: str, proxy: Optional[dict] = None, verify: log_stream.get("href") if log_stream is not None else None, ) + @staticmethod + def build_from_cf_config(config_path: Optional[str] = None, **kwargs) -> 'CloudFoundryClient': + config = Path(config_path) if config_path else Path.home() / '.cf/config.json' + try: + with open(config) as f: + cf_config = json.load(f) + except Exception as e: + _logger.critical('Could not retrieve cf config: %s', e) + raise + client = CloudFoundryClient(cf_config['Target'], **kwargs) + client.init_with_token(cf_config['RefreshToken']) + return client + @staticmethod def _resolve_login_endpoint(root_links): return (root_links.get("login") or root_links.get("uaa") or root_links.get("self"))["href"] diff --git a/test/test_client.py b/test/test_client.py index 7d659e8..b53d6de 100644 --- a/test/test_client.py +++ b/test/test_client.py @@ -1,4 +1,5 @@ import json +from os import remove as file_remove import unittest from http import HTTPStatus from unittest.mock import patch @@ -89,6 +90,34 @@ def test_refresh_request_with_token_format_opaque(self): verify=True, ) + def test_refresh_request_with_token_from_cf_config(self): + requests = FakeRequests() + session = MockSession() + proxy = dict(http='', https='') + cf_config_file_content = {"Target": self.TARGET_ENDPOINT, "RefreshToken": "refresh-token"} + with open("config.json", "w", encoding="utf-8") as f: + json.dump(cf_config_file_content, f, ensure_ascii=False, indent=4) + with patch("oauth2_client.credentials_manager.requests", new=requests), patch( + "cloudfoundry_client.client.requests", new=requests + ): + requests.Session.return_value = session + self._mock_info_calls(requests) + requests.post.return_value = MockResponse( + "%s/oauth/token" % self.AUTHORIZATION_ENDPOINT, + status_code=HTTPStatus.OK.value, + text=json.dumps(dict(access_token="access-token", refresh_token="refresh-token")), + ) + client = CloudFoundryClient.build_from_cf_config("config.json", proxy=proxy, verify=True) # noqa: F841 + file_remove('config.json') + self.assertEqual("Bearer access-token", session.headers.get("Authorization")) + requests.post.assert_called_with( + requests.post.return_value.url, + data=dict(grant_type="refresh_token", scope="", refresh_token="refresh-token"), + headers=dict(Accept="application/json", Authorization="Basic Y2Y6"), + proxies=proxy, + verify=True, + ) + def test_grant_password_request_with_login_hint(self): requests = FakeRequests() session = MockSession() From 078dcfd10bfda4d3befced00b9d41a58de8e9149 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Mon, 27 Jun 2022 17:37:21 +0200 Subject: [PATCH 138/264] Allow to get number of entities on entities generator Close #168 --- main/cloudfoundry_client/common_objects.py | 53 ++++++++++++++ main/cloudfoundry_client/json_object.py | 8 --- .../main/command_domain.py | 2 +- main/cloudfoundry_client/main/main.py | 2 +- .../main/tasks_command_domain.py | 2 +- .../networking/entities.py | 3 +- main/cloudfoundry_client/request_object.py | 4 -- main/cloudfoundry_client/v2/apps.py | 8 +-- main/cloudfoundry_client/v2/entities.py | 37 +++++----- main/cloudfoundry_client/v2/jobs.py | 2 +- main/cloudfoundry_client/v2/resources.py | 2 +- main/cloudfoundry_client/v2/service_plans.py | 5 +- main/cloudfoundry_client/v3/apps.py | 2 +- main/cloudfoundry_client/v3/domains.py | 5 +- main/cloudfoundry_client/v3/entities.py | 71 +++++++++++-------- test/test_request_object.py | 2 +- test/v2/test_entities.py | 63 +++++++++++++++- test/v3/test_apps.py | 2 +- test/v3/test_entities.py | 51 +++++++++++++ 19 files changed, 243 insertions(+), 81 deletions(-) create mode 100644 main/cloudfoundry_client/common_objects.py delete mode 100644 main/cloudfoundry_client/json_object.py delete mode 100644 main/cloudfoundry_client/request_object.py create mode 100644 test/v3/test_entities.py diff --git a/main/cloudfoundry_client/common_objects.py b/main/cloudfoundry_client/common_objects.py new file mode 100644 index 0000000..9c3aa0f --- /dev/null +++ b/main/cloudfoundry_client/common_objects.py @@ -0,0 +1,53 @@ +import json +from typing import Callable, TypeVar, Generic, List, Union + + +class Request(dict): + def __setitem__(self, key, value): + if value is not None: + super(Request, self).__setitem__(key, value) + + +class JsonObject(dict): + def __init__(self, *args, **kwargs): + super(JsonObject, self).__init__(*args, **kwargs) + + json = json.dumps + + +ENTITY = TypeVar('ENTITY') + + +class Pagination(Generic[ENTITY]): + def __init__(self, first_page: JsonObject, + total_result: int, + next_page_loader: Callable[[JsonObject], Union[None, JsonObject]], + resources_accessor: Callable[[JsonObject], List[JsonObject]], + instance_creator: Callable[[JsonObject], ENTITY]): + self._first_page = first_page + self._total_results = total_result + self._next_page_loader = next_page_loader + self._resources_accessor = resources_accessor + self._instance_creator = instance_creator + self._cursor = None + self._current_page = None + + @property + def total_results(self) -> int: + return self._total_results + + def __iter__(self): + return self + + def __next__(self) -> ENTITY: + try: + if self._cursor is None: + self._current_page = self._first_page + self._cursor = self._resources_accessor(self._current_page).__iter__() + return self._instance_creator(self._cursor.__next__()) + except StopIteration: + self._current_page = self._next_page_loader(self._current_page) + if self._current_page is None: + raise + self._cursor = self._resources_accessor(self._current_page).__iter__() + return self._instance_creator(self._cursor.__next__()) diff --git a/main/cloudfoundry_client/json_object.py b/main/cloudfoundry_client/json_object.py deleted file mode 100644 index 590b9f3..0000000 --- a/main/cloudfoundry_client/json_object.py +++ /dev/null @@ -1,8 +0,0 @@ -import json - - -class JsonObject(dict): - def __init__(self, *args, **kwargs): - super(JsonObject, self).__init__(*args, **kwargs) - - json = json.dumps diff --git a/main/cloudfoundry_client/main/command_domain.py b/main/cloudfoundry_client/main/command_domain.py index 5a80e3b..334b019 100644 --- a/main/cloudfoundry_client/main/command_domain.py +++ b/main/cloudfoundry_client/main/command_domain.py @@ -9,7 +9,7 @@ from cloudfoundry_client.client import CloudFoundryClient from cloudfoundry_client.errors import InvalidStatusCode -from cloudfoundry_client.json_object import JsonObject +from cloudfoundry_client.common_objects import JsonObject class Command(object): diff --git a/main/cloudfoundry_client/main/main.py b/main/cloudfoundry_client/main/main.py index 7f06253..bed8a33 100644 --- a/main/cloudfoundry_client/main/main.py +++ b/main/cloudfoundry_client/main/main.py @@ -13,7 +13,7 @@ from cloudfoundry_client import __version__ from cloudfoundry_client.client import CloudFoundryClient from cloudfoundry_client.errors import InvalidStatusCode -from cloudfoundry_client.json_object import JsonObject +from cloudfoundry_client.common_objects import JsonObject from cloudfoundry_client.main.apps_command_domain import AppCommandDomain from cloudfoundry_client.main.command_domain import CommandDomain, Command from cloudfoundry_client.main.operation_commands import generate_push_command diff --git a/main/cloudfoundry_client/main/tasks_command_domain.py b/main/cloudfoundry_client/main/tasks_command_domain.py index dc76a9d..9a7c623 100644 --- a/main/cloudfoundry_client/main/tasks_command_domain.py +++ b/main/cloudfoundry_client/main/tasks_command_domain.py @@ -3,7 +3,7 @@ from argparse import Namespace, _SubParsersAction from cloudfoundry_client.client import CloudFoundryClient -from cloudfoundry_client.json_object import JsonObject +from cloudfoundry_client.common_objects import JsonObject from cloudfoundry_client.main.command_domain import CommandDomain, Command diff --git a/main/cloudfoundry_client/networking/entities.py b/main/cloudfoundry_client/networking/entities.py index d68de5f..da393d6 100644 --- a/main/cloudfoundry_client/networking/entities.py +++ b/main/cloudfoundry_client/networking/entities.py @@ -6,8 +6,7 @@ from requests import Response from cloudfoundry_client.errors import InvalidEntity -from cloudfoundry_client.json_object import JsonObject -from cloudfoundry_client.request_object import Request +from cloudfoundry_client.common_objects import JsonObject, Request if TYPE_CHECKING: from cloudfoundry_client.client import CloudFoundryClient diff --git a/main/cloudfoundry_client/request_object.py b/main/cloudfoundry_client/request_object.py deleted file mode 100644 index f0d2152..0000000 --- a/main/cloudfoundry_client/request_object.py +++ /dev/null @@ -1,4 +0,0 @@ -class Request(dict): - def __setitem__(self, key, value): - if value is not None: - super(Request, self).__setitem__(key, value) diff --git a/main/cloudfoundry_client/v2/apps.py b/main/cloudfoundry_client/v2/apps.py index 64a93a0..0ffe16f 100644 --- a/main/cloudfoundry_client/v2/apps.py +++ b/main/cloudfoundry_client/v2/apps.py @@ -7,8 +7,8 @@ from cloudfoundry_client.doppler.client import EnvelopeStream from cloudfoundry_client.errors import InvalidStatusCode -from cloudfoundry_client.json_object import JsonObject -from cloudfoundry_client.v2.entities import Entity, EntityManager, PaginateEntities +from cloudfoundry_client.common_objects import JsonObject, Pagination +from cloudfoundry_client.v2.entities import Entity, EntityManager if TYPE_CHECKING: from cloudfoundry_client.client import CloudFoundryClient @@ -95,13 +95,13 @@ def get_summary(self, application_guid: str) -> JsonObject: def associate_route(self, application_guid: str, route_guid: str) -> Application: self._put("%s%s/%s/routes/%s" % (self.target_endpoint, self.entity_uri, application_guid, route_guid)) - def list_routes(self, application_guid: str, **kwargs) -> PaginateEntities: + def list_routes(self, application_guid: str, **kwargs) -> Pagination[Entity]: return self.client.v2.routes._list("%s/%s/routes" % (self.entity_uri, application_guid), **kwargs) def remove_route(self, application_guid: str, route_guid: str): self._delete("%s%s/%s/routes/%s" % (self.target_endpoint, self.entity_uri, application_guid, route_guid)) - def list_service_bindings(self, application_guid: str, **kwargs) -> PaginateEntities: + def list_service_bindings(self, application_guid: str, **kwargs) -> Pagination[Entity]: return self.client.v2.service_bindings._list("%s/%s/service_bindings" % (self.entity_uri, application_guid), **kwargs) def start( diff --git a/main/cloudfoundry_client/v2/entities.py b/main/cloudfoundry_client/v2/entities.py index 1c45875..d9ace97 100644 --- a/main/cloudfoundry_client/v2/entities.py +++ b/main/cloudfoundry_client/v2/entities.py @@ -1,11 +1,10 @@ from functools import partial, reduce -from typing import Callable, List, Tuple, Any, Optional, Generator, TYPE_CHECKING +from typing import Callable, List, Tuple, Any, Optional, TYPE_CHECKING, Union from urllib.parse import quote from requests import Response from cloudfoundry_client.errors import InvalidEntity -from cloudfoundry_client.json_object import JsonObject -from cloudfoundry_client.request_object import Request +from cloudfoundry_client.common_objects import JsonObject, Request, Pagination if TYPE_CHECKING: from cloudfoundry_client.client import CloudFoundryClient @@ -42,8 +41,6 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient", *args, ** EntityBuilder = Callable[[List[Tuple[str, Any]]], Entity] -PaginateEntities = Generator[Entity, None, None] - class EntityManager(object): list_query_parameters = ["page", "results-per-page", "order-direction"] @@ -62,19 +59,21 @@ def __init__( entity_builder if entity_builder is not None else lambda pairs: Entity(target_endpoint, client, pairs) ) - def _list(self, requested_path: str, entity_builder: Optional[EntityBuilder] = None, **kwargs) -> PaginateEntities: + def _list(self, requested_path: str, entity_builder: Optional[EntityBuilder] = None, **kwargs) -> Pagination[Entity]: url_requested = self._get_url_filtered("%s%s" % (self.target_endpoint, requested_path), **kwargs) - response = self.client.get(url_requested) - entity_builder = self._get_entity_builder(entity_builder) - while True: - response_json = self._read_response(response, JsonObject) - for resource in response_json["resources"]: - yield entity_builder(list(resource.items())) - if response_json["next_url"] is None: - break - else: - url_requested = "%s%s" % (self.target_endpoint, response_json["next_url"]) - response = self.client.get(url_requested) + current_builder = self._get_entity_builder(entity_builder) + response_json = self._read_response(self.client.get(url_requested), JsonObject) + return Pagination(response_json, response_json.get("total_results", 0), + self._next_page, + lambda page: page["resources"], + lambda json_object: current_builder(list(json_object.items()))) + + def _next_page(self, current_page: JsonObject) -> Union[None, JsonObject]: + next_url = current_page.get("next_url") + if next_url is None: + return None + url_requested = "%s%s" % (self.target_endpoint, next_url) + return self._read_response(self.client.get(url_requested), JsonObject) def _create(self, data: dict, **kwargs) -> Entity: url = "%s%s" % (self.target_endpoint, self.entity_uri) @@ -104,13 +103,13 @@ def _put(self, url: str, data: Optional[dict] = None, **kwargs): def _delete(self, url: str, **kwargs): self.client.delete(url, **kwargs) - def __iter__(self) -> PaginateEntities: + def __iter__(self) -> Pagination[Entity]: return self.list() def __getitem__(self, entity_guid) -> Entity: return self.get(entity_guid) - def list(self, **kwargs) -> PaginateEntities: + def list(self, **kwargs) -> Pagination[Entity]: return self._list(self.entity_uri, **kwargs) def get_first(self, **kwargs) -> Optional[Entity]: diff --git a/main/cloudfoundry_client/v2/jobs.py b/main/cloudfoundry_client/v2/jobs.py index 0d6f30f..68bdaed 100644 --- a/main/cloudfoundry_client/v2/jobs.py +++ b/main/cloudfoundry_client/v2/jobs.py @@ -1,6 +1,6 @@ from typing import TYPE_CHECKING -from cloudfoundry_client.json_object import JsonObject +from cloudfoundry_client.common_objects import JsonObject if TYPE_CHECKING: from cloudfoundry_client.client import CloudFoundryClient diff --git a/main/cloudfoundry_client/v2/resources.py b/main/cloudfoundry_client/v2/resources.py index 739c188..d962daa 100644 --- a/main/cloudfoundry_client/v2/resources.py +++ b/main/cloudfoundry_client/v2/resources.py @@ -1,6 +1,6 @@ from typing import List, TYPE_CHECKING -from cloudfoundry_client.json_object import JsonObject +from cloudfoundry_client.common_objects import JsonObject if TYPE_CHECKING: from cloudfoundry_client.client import CloudFoundryClient diff --git a/main/cloudfoundry_client/v2/service_plans.py b/main/cloudfoundry_client/v2/service_plans.py index 487b1cd..9a94aa8 100644 --- a/main/cloudfoundry_client/v2/service_plans.py +++ b/main/cloudfoundry_client/v2/service_plans.py @@ -1,6 +1,7 @@ from typing import TYPE_CHECKING -from cloudfoundry_client.v2.entities import EntityManager, Entity, PaginateEntities +from cloudfoundry_client.common_objects import Pagination +from cloudfoundry_client.v2.entities import EntityManager, Entity if TYPE_CHECKING: from cloudfoundry_client.client import CloudFoundryClient @@ -13,5 +14,5 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): def create_from_resource_file(self, path: str) -> Entity: raise NotImplementedError("No creation allowed") - def list_instances(self, service_plan_guid: str, **kwargs) -> PaginateEntities: + def list_instances(self, service_plan_guid: str, **kwargs) -> Pagination[Entity]: return self.client.v2.service_instances._list("%s/%s/service_instances" % (self.entity_uri, service_plan_guid), **kwargs) diff --git a/main/cloudfoundry_client/v3/apps.py b/main/cloudfoundry_client/v3/apps.py index 272dff4..1453575 100644 --- a/main/cloudfoundry_client/v3/apps.py +++ b/main/cloudfoundry_client/v3/apps.py @@ -1,6 +1,6 @@ from typing import TYPE_CHECKING, Optional -from cloudfoundry_client.json_object import JsonObject +from cloudfoundry_client.common_objects import JsonObject from cloudfoundry_client.v3.entities import EntityManager if TYPE_CHECKING: diff --git a/main/cloudfoundry_client/v3/domains.py b/main/cloudfoundry_client/v3/domains.py index 2ffe42c..d1f5db3 100644 --- a/main/cloudfoundry_client/v3/domains.py +++ b/main/cloudfoundry_client/v3/domains.py @@ -1,6 +1,7 @@ from typing import Optional, TYPE_CHECKING -from cloudfoundry_client.v3.entities import EntityManager, ToOneRelationship, ToManyRelationship, PaginateEntities, Entity +from cloudfoundry_client.common_objects import Pagination +from cloudfoundry_client.v3.entities import EntityManager, ToOneRelationship, ToManyRelationship, Entity if TYPE_CHECKING: from cloudfoundry_client.client import CloudFoundryClient @@ -42,7 +43,7 @@ def create( } return super(DomainManager, self)._create(data) - def list_domains_for_org(self, org_guid: str, **kwargs) -> PaginateEntities: + def list_domains_for_org(self, org_guid: str, **kwargs) -> Pagination[Entity]: uri = "/v3/organizations/{guid}/domains".format(guid=org_guid) return self._list(uri, **kwargs) diff --git a/main/cloudfoundry_client/v3/entities.py b/main/cloudfoundry_client/v3/entities.py index 615a881..36ec5f4 100644 --- a/main/cloudfoundry_client/v3/entities.py +++ b/main/cloudfoundry_client/v3/entities.py @@ -1,13 +1,12 @@ import functools from json import JSONDecodeError -from typing import Any, Generator, Optional, List, Tuple, Union, TypeVar, TYPE_CHECKING, Callable, Type +from typing import Any, Optional, List, Tuple, Union, TypeVar, TYPE_CHECKING, Callable, Type from urllib.parse import quote, urlparse from requests import Response from cloudfoundry_client.errors import InvalidEntity -from cloudfoundry_client.json_object import JsonObject -from cloudfoundry_client.request_object import Request +from cloudfoundry_client.common_objects import JsonObject, Request, Pagination if TYPE_CHECKING: from cloudfoundry_client.client import CloudFoundryClient, V3 @@ -79,9 +78,6 @@ def _manager_method(link_name: str, link_method: str) -> Optional[str]: return None -PaginateEntities = Generator[Entity, None, None] - - class Relationship(JsonObject): def __init__(self, guid: Optional[str]): super(Relationship, self).__init__(guid=guid) @@ -169,38 +165,39 @@ def _extract_job_guid(job_location): job_guid = job_url.path.rsplit("/", 1)[-1] return job_guid - def _list(self, requested_path: str, entity_type: Optional[ENTITY_TYPE] = None, **kwargs) -> PaginateEntities: + def _list(self, requested_path: str, entity_type: Optional[ENTITY_TYPE] = None, **kwargs) -> Pagination[Entity]: url_requested = EntityManager._get_url_with_encoded_params("%s%s" % (self.target_endpoint, requested_path), **kwargs) - for element in self._paginate(url_requested, entity_type): - yield element - - def _paginate(self, url_requested: str, entity_type: Optional[ENTITY_TYPE] = None) -> PaginateEntities: response_json = self._read_response(self.client.get(url_requested), JsonObject) - yield from self._crawl_pagination(entity_type, response_json) + return self._pagination(response_json, entity_type) def _attempt_to_paginate(self, url_requested: str, entity_type: Optional[ENTITY_TYPE] = None) \ - -> Union[PaginateEntities, Entity]: + -> Union[Pagination[Entity], Entity]: response_json = self._read_response(self.client.get(url_requested), JsonObject) if "resources" in response_json: - return self._crawl_pagination(entity_type, response_json) + return self._pagination(response_json, entity_type) else: return response_json - def _crawl_pagination(self, entity_type, response_json) -> PaginateEntities: - while True: - for resource in response_json["resources"]: - yield self._entity(resource, entity_type) - pagination = response_json.get("pagination") - if ( - pagination is None - or "next" not in pagination - or pagination["next"] is None - or pagination["next"].get("href") is None - ): - break - else: - url_requested = response_json["pagination"]["next"]["href"] - response_json = self._read_response(self.client.get(url_requested), JsonObject) + def _pagination(self, page: JsonObject, entity_type: Optional[ENTITY_TYPE] = None) -> Pagination[Entity]: + def _entity(json_object: JsonObject) -> Entity: + return self._entity(json_object, entity_type) + + return Pagination(page, + page.get("pagination", {}).get("total_results", 0), + self._next_page, + lambda p: p["resources"], + _entity) + + def _next_page(self, current_page: JsonObject) -> Union[None, JsonObject]: + pagination = current_page.get("pagination") + if ( + pagination is None + or "next" not in pagination + or pagination["next"] is None + or pagination["next"].get("href") is None + ): + return None + return self._read_response(self.client.get(current_page["pagination"]["next"]["href"]), JsonObject) def _create(self, data: dict) -> Entity: url = "%s%s" % (self.target_endpoint, self.entity_uri) @@ -215,13 +212,25 @@ def _update(self, resource_id: str, data: dict) -> Entity: url = "%s%s/%s" % (self.target_endpoint, self.entity_uri, resource_id) return self._patch(url, data) - def __iter__(self) -> PaginateEntities: + def __iter__(self) -> Pagination[Entity]: return self.list() def __getitem__(self, entity_guid) -> Entity: return self.get(entity_guid) - def list(self, **kwargs) -> PaginateEntities: + def __len__(self): + return self.len() + + def len(self, **kwargs): + url_requested = EntityManager._get_url_with_encoded_params("%s%s" % (self.target_endpoint, self.entity_uri), **kwargs) + response_json = self._read_response(self.client.get(url_requested, JsonObject)) + pagination = response_json.get("pagination") + if pagination is not None: + return pagination.get("total_results", 0) + else: + return 0 + + def list(self, **kwargs) -> Pagination[Entity]: return self._list(self.entity_uri, **kwargs) def get_first(self, **kwargs) -> Optional[Entity]: diff --git a/test/test_request_object.py b/test/test_request_object.py index 4562f24..1c24deb 100644 --- a/test/test_request_object.py +++ b/test/test_request_object.py @@ -1,6 +1,6 @@ import unittest -from cloudfoundry_client.request_object import Request +from cloudfoundry_client.common_objects import Request class TestRequest(unittest.TestCase): diff --git a/test/v2/test_entities.py b/test/v2/test_entities.py index 9f7d352..35427d8 100644 --- a/test/v2/test_entities.py +++ b/test/v2/test_entities.py @@ -5,7 +5,7 @@ from abstract_test_case import AbstractTestCase from cloudfoundry_client.errors import InvalidEntity -from cloudfoundry_client.v2.entities import EntityManager +from cloudfoundry_client.v2.entities import EntityManager, Entity class TestEntities(unittest.TestCase, AbstractTestCase): @@ -99,6 +99,32 @@ def test_list(self): client.get.assert_has_calls([call(first_response.url), call(second_response.url)], any_order=False) self.assertEqual(cpt, 3) + def test_elements_are_entities(self): + client = MagicMock() + entity_manager = EntityManager(self.TARGET_ENDPOINT, client, "/fake/first") + + first_response = self.mock_response( + "/fake/first?order-direction=asc&page=1&results-per-page=20&q=space_guid%3Asome-id", + HTTPStatus.OK, + None, + "fake", + "GET_multi_page_0_response.json", + ) + second_response = self.mock_response( + "/fake/next?order-direction=asc&page=2&results-per-page=50", + HTTPStatus.OK, + None, + "fake", + "GET_multi_page_1_response.json", + ) + client.get.side_effect = [first_response, second_response] + + entities_list = entity_manager.list( + **{"results-per-page": 20, "order-direction": "asc", "page": 1, "space_guid": "some-id"}) + + for entity in entities_list: + self.assertIsInstance(entity, Entity) + def test_iter(self): client = MagicMock() entity_manager = EntityManager(self.TARGET_ENDPOINT, client, "/fake/something") @@ -120,3 +146,38 @@ def test_get_elem(self): client.get.assert_called_with(client.get.return_value.url) self.assertEqual(entity["entity"]["name"], "name-423") + + def test_entity_manager_is_a_generator(self): + client = MagicMock() + entity_manager = EntityManager(self.TARGET_ENDPOINT, client, "/fake/something") + client.get.return_value = self.mock_response( + "/fake/something/with-id", HTTPStatus.OK, None, "fake", "GET_{id}_response.json" + ) + + self.assertIsNotNone(getattr(entity_manager, "__iter__", None)) + self.assertIsNotNone(getattr(entity_manager.__iter__, "__call__", None)) + generator = entity_manager.__iter__() + self.assertIsNotNone(getattr(generator, "__next__", None)) + self.assertIsNotNone(getattr(generator.__next__, "__call__", None)) + + def test_entity_list_is_a_generator(self): + client = MagicMock() + entity_manager = EntityManager(self.TARGET_ENDPOINT, client, "/fake/something") + client.get.return_value = self.mock_response( + "/fake/something/with-id", HTTPStatus.OK, None, "fake", "GET_{id}_response.json" + ) + + generator = entity_manager.list() + + self.assertIsNotNone(getattr(generator, "__next__", None)) + self.assertIsNotNone(getattr(generator.__next__, "__call__", None)) + + def test_total_results(self): + client = MagicMock() + entity_manager = EntityManager(self.TARGET_ENDPOINT, client, "/fake/something") + client.get.return_value = self.mock_response("/fake/something", HTTPStatus.OK, None, "fake", "GET_response.json") + + cpt = entity_manager.list().total_results + + self.assertEqual(cpt, 3) + client.get.assert_called_with(client.get.return_value.url) diff --git a/test/v3/test_apps.py b/test/v3/test_apps.py index 98e642e..1130e18 100644 --- a/test/v3/test_apps.py +++ b/test/v3/test_apps.py @@ -2,7 +2,7 @@ from http import HTTPStatus from abstract_test_case import AbstractTestCase -from cloudfoundry_client.json_object import JsonObject +from cloudfoundry_client.common_objects import JsonObject from cloudfoundry_client.v3.entities import Entity diff --git a/test/v3/test_entities.py b/test/v3/test_entities.py new file mode 100644 index 0000000..4daee4f --- /dev/null +++ b/test/v3/test_entities.py @@ -0,0 +1,51 @@ +import unittest +from http import HTTPStatus +from unittest.mock import MagicMock + +from abstract_test_case import AbstractTestCase +from cloudfoundry_client.v3.entities import EntityManager, Entity + + +class TestEntities(unittest.TestCase, AbstractTestCase): + def test_len(self): + client = MagicMock() + client.get.return_value = self.mock_response("/fake/something", HTTPStatus.OK, None, "v3", "apps", "GET_response.json") + + entity_manager = EntityManager(self.TARGET_ENDPOINT, client, "/fake/something") + + cpt = entity_manager.list().total_results + + self.assertEqual(cpt, 3) + client.get.assert_called_with(client.get.return_value.url) + + def test_entity_manager_is_a_generator(self): + client = MagicMock() + client.get.return_value = self.mock_response("/fake/something", HTTPStatus.OK, None, "v3", "apps", "GET_response.json") + + entity_manager = EntityManager(self.TARGET_ENDPOINT, client, "/fake/something") + + self.assertIsNotNone(getattr(entity_manager, "__iter__", None)) + self.assertIsNotNone(getattr(entity_manager.__iter__, "__call__", None)) + generator = entity_manager.__iter__() + self.assertIsNotNone(getattr(generator, "__next__", None)) + self.assertIsNotNone(getattr(generator.__next__, "__call__", None)) + + def test_entity_list_is_a_generator(self): + client = MagicMock() + client.get.return_value = self.mock_response("/fake/something", HTTPStatus.OK, None, "v3", "apps", "GET_response.json") + entity_manager = EntityManager(self.TARGET_ENDPOINT, client, "/fake/something") + + generator = entity_manager.list() + + self.assertIsNotNone(getattr(generator, "__next__", None)) + self.assertIsNotNone(getattr(generator.__next__, "__call__", None)) + + def test_elements_are_entities(self): + client = MagicMock() + client.get.return_value = self.mock_response("/fake/something", HTTPStatus.OK, None, "v3", "apps", "GET_response.json") + entity_manager = EntityManager(self.TARGET_ENDPOINT, client, "/fake/something") + + entities_list = entity_manager.list() + + for entity in entities_list: + self.assertIsInstance(entity, Entity) From 00b9566e1f7155729a37bb1cf6f02906e2633839 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sat, 3 Dec 2022 18:19:05 +0100 Subject: [PATCH 139/264] Fixes 'disk_quota' convertion --- .../operations/push/validation/manifest.py | 39 +++++++++---------- .../push/validation/test_manifest_reader.py | 19 +++++---- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/main/cloudfoundry_client/operations/push/validation/manifest.py b/main/cloudfoundry_client/operations/push/validation/manifest.py index 94c93dc..326bfcf 100644 --- a/main/cloudfoundry_client/operations/push/validation/manifest.py +++ b/main/cloudfoundry_client/operations/push/validation/manifest.py @@ -6,7 +6,7 @@ class ManifestReader(object): - MEMORY_PATTERN = re.compile(r"^(\d+)([KMGT])B?$") + SIZE_FIELD_PATTERNS = re.compile(r"^(\d+)([MG])B?$") POSITIVE_FIELDS = ["instances", "timeout"] @@ -40,7 +40,7 @@ def _validate_application_manifest(manifest_directory: str, app_manifest: dict): raise AssertionError("One of path or docker must be set") else: ManifestReader._absolute_path(manifest_directory, app_manifest) - ManifestReader._convert_memory(app_manifest) + ManifestReader._convert_size_fields(app_manifest) for field in ManifestReader.POSITIVE_FIELDS: ManifestReader._convert_positive(app_manifest, field) for field in ManifestReader.BOOLEAN_FIELDS: @@ -61,25 +61,22 @@ def _check_deprecated_attributes(app_manifest: dict): raise AssertionError("hosts, host, domains, domain and no-hostname are all deprecated. Use the routes attribute") @staticmethod - def _convert_memory(manifest: dict): - if "memory" in manifest: - memory = manifest["memory"].upper() - match = ManifestReader.MEMORY_PATTERN.match(memory) - if match is None: - raise AssertionError("Invalid memory format: %s" % memory) - - memory_number = int(match.group(1)) - if match.group(2) == "K": - memory_number *= 1024 - elif match.group(2) == "M": - memory_number *= 1024 * 1024 - elif match.group(2) == "G": - memory_number *= 1024 * 1024 * 1024 - elif match.group(2) == "T": - memory_number *= 1024 * 1024 * 1024 * 1024 - else: - raise AssertionError("Invalid memory unit: %s" % memory) - manifest["memory"] = int(memory_number / (1024 * 1024)) + def _convert_size_fields(manifest: dict): + for field_name in ["memory", "disk_quota"]: + if field_name in manifest: + field_value = manifest[field_name].upper() + match = ManifestReader.SIZE_FIELD_PATTERNS.match(field_value) + if match is None: + raise AssertionError("Invalid %s format: %s" % (field_name, field_value)) + + size_converted = int(match.group(1)) + if match.group(2) == "M": + size_converted *= 1024 * 1024 + elif match.group(2) == "G": + size_converted *= 1024 * 1024 * 1024 + else: + raise AssertionError("Invalid %s unit: %s" % (field_name, field_value)) + manifest[field_name] = int(size_converted / (1024 * 1024)) @staticmethod def _convert_positive(manifest: dict, field: str): diff --git a/test/operations/push/validation/test_manifest_reader.py b/test/operations/push/validation/test_manifest_reader.py index 25995c3..0182cf2 100644 --- a/test/operations/push/validation/test_manifest_reader.py +++ b/test/operations/push/validation/test_manifest_reader.py @@ -104,17 +104,22 @@ def path_should_be_set_as_absolute(self): ManifestReader._validate_application_manifest(".", manifest) self.assertEqual(os.path.abspath("test"), manifest["path"]) - def test_memory_in_kb(self): - manifest = dict(memory="2048KB") - ManifestReader._convert_memory(manifest) - self.assertEqual(2, manifest["memory"]) - def test_memory_in_mb(self): manifest = dict(memory="2048MB") - ManifestReader._convert_memory(manifest) + ManifestReader._convert_size_fields(manifest) self.assertEqual(2048, manifest["memory"]) def test_memory_in_gb(self): manifest = dict(memory="1G") - ManifestReader._convert_memory(manifest) + ManifestReader._convert_size_fields(manifest) self.assertEqual(1024, manifest["memory"]) + + def test_disk_quota_in_mb(self): + manifest = dict(disk_quota="2048MB") + ManifestReader._convert_size_fields(manifest) + self.assertEqual(2048, manifest["disk_quota"]) + + def test_disk_quota_in_gb(self): + manifest = dict(disk_quota="1G") + ManifestReader._convert_size_fields(manifest) + self.assertEqual(1024, manifest["disk_quota"]) From 929167ebb00dad7a285f4ba895000a2eba18927b Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sat, 3 Dec 2022 18:32:26 +0100 Subject: [PATCH 140/264] Implement delete unmapped routes --- main/cloudfoundry_client/client.py | 3 ++- main/cloudfoundry_client/v2/spaces.py | 16 ++++++++++++++++ test/v2/test_spaces.py | 6 ++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 main/cloudfoundry_client/v2/spaces.py diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index a49230a..8521373 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -25,6 +25,7 @@ from cloudfoundry_client.v2.service_keys import ServiceKeyManager from cloudfoundry_client.v2.service_plan_visibilities import ServicePlanVisibilityManager from cloudfoundry_client.v2.service_plans import ServicePlanManager as ServicePlanManagerV2 +from cloudfoundry_client.v2.spaces import SpaceManager as SpaceManagerV2 from cloudfoundry_client.v3.apps import AppManager from cloudfoundry_client.v3.buildpacks import BuildpackManager @@ -87,7 +88,7 @@ def __init__(self, target_endpoint: str, credential_manager: "CloudFoundryClient self.routes = RouteManager(target_endpoint, credential_manager) self.services = EntityManagerV2(target_endpoint, credential_manager, "/v2/services") self.shared_domains = EntityManagerV2(target_endpoint, credential_manager, "/v2/shared_domains") - self.spaces = EntityManagerV2(target_endpoint, credential_manager, "/v2/spaces") + self.spaces = SpaceManagerV2(target_endpoint, credential_manager) self.stacks = EntityManagerV2(target_endpoint, credential_manager, "/v2/stacks") self.user_provided_service_instances = EntityManagerV2( target_endpoint, credential_manager, "/v2/user_provided_service_instances" diff --git a/main/cloudfoundry_client/v2/spaces.py b/main/cloudfoundry_client/v2/spaces.py new file mode 100644 index 0000000..b775529 --- /dev/null +++ b/main/cloudfoundry_client/v2/spaces.py @@ -0,0 +1,16 @@ +from typing import TYPE_CHECKING + +from cloudfoundry_client.v2.entities import EntityManager + +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + + +class SpaceManager(EntityManager): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(SpaceManager, self).__init__(target_endpoint, client, "/v2/spaces") + + def delete_unmapped_routes(self, space_guid: str): + url = "%s%s/%s/unmapped_routes" % (self.target_endpoint, self.entity_uri, space_guid) + super(SpaceManager, self)._delete(url) + diff --git a/test/v2/test_spaces.py b/test/v2/test_spaces.py index 8307318..42ac1f2 100644 --- a/test/v2/test_spaces.py +++ b/test/v2/test_spaces.py @@ -63,6 +63,12 @@ def test_entity(self): self.assertEqual(cpt, 1) self.client.get.assert_has_calls([call(side_effect.url) for side_effect in self.client.get.side_effect], any_order=False) + def test_delete_unmapped_routes(self): + self.client.delete.return_value = self.mock_response( + "/v2/spaces/space_id/unmapped_routes", HTTPStatus.NO_CONTENT, None) + self.client.v2.spaces.delete_unmapped_routes("space_id") + self.client.delete.assert_called_with(self.client.delete.return_value.url) + @patch.object(sys, "argv", ["main", "list_spaces"]) def test_main_list_spaces(self): with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): From 58423811a17c883727433ce50584ec37a447c0d2 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Thu, 2 Feb 2023 15:38:39 +0100 Subject: [PATCH 141/264] Prepare version 1.31.0 --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index b300eef..6233636 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.30.0" +__version__ = "1.31.0" From 2e7d19c8f31a3a28f70062dbe72a3502e3d0e4d9 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Thu, 2 Feb 2023 16:30:36 +0100 Subject: [PATCH 142/264] Bump flake8 --- .flake8 | 2 +- dev-requirements.txt | 4 ++-- main/cloudfoundry_client/v2/spaces.py | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.flake8 b/.flake8 index 5099cb2..8f714bc 100644 --- a/.flake8 +++ b/.flake8 @@ -3,5 +3,5 @@ ignore = E203, W503 max-line-length = 130 max-complexity = 16 buildins = CloudFoundryClient -exclude = vendors,.git,.github,main/cloudfoundry_client/dropsonde +exclude = vendors,.git,.github,main/cloudfoundry_client/dropsonde,venv,.eggs,build diff --git a/dev-requirements.txt b/dev-requirements.txt index d6762e6..0889174 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,2 +1,2 @@ -black==20.8b1 -flake8==3.8.4 +black==22.12.0 +flake8==5.0.4 diff --git a/main/cloudfoundry_client/v2/spaces.py b/main/cloudfoundry_client/v2/spaces.py index b775529..6f6254a 100644 --- a/main/cloudfoundry_client/v2/spaces.py +++ b/main/cloudfoundry_client/v2/spaces.py @@ -13,4 +13,3 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): def delete_unmapped_routes(self, space_guid: str): url = "%s%s/%s/unmapped_routes" % (self.target_endpoint, self.entity_uri, space_guid) super(SpaceManager, self)._delete(url) - From 528029caf0e508d859e1f86007f7388f63b509a5 Mon Sep 17 00:00:00 2001 From: Florian Braun <5863788+FloThinksPi@users.noreply.github.com> Date: Mon, 9 Jan 2023 14:21:22 +0100 Subject: [PATCH 143/264] Bump Protobuff library to remove conflicts Bumping this libary resolves many conflicats as other libraries start depending on protobuf 4 in an incompatible way --- .../cloudfoundry_client/dropsonde/__init__.py | 3 + .../dropsonde/envelope_pb2.py | 271 +---------- .../dropsonde/error_pb2.py | 83 +--- .../cloudfoundry_client/dropsonde/http_pb2.py | 434 +----------------- main/cloudfoundry_client/dropsonde/log_pb2.py | 131 +----- .../dropsonde/metric_pb2.py | 223 +-------- .../cloudfoundry_client/dropsonde/uuid_pb2.py | 76 +-- requirements.txt | 2 +- setup.py | 12 +- vendors/dropsonde-protocol/envelope.proto | 1 + vendors/dropsonde-protocol/error.proto | 1 + vendors/dropsonde-protocol/http.proto | 1 + vendors/dropsonde-protocol/log.proto | 1 + vendors/dropsonde-protocol/metric.proto | 3 +- vendors/dropsonde-protocol/uuid.proto | 1 + 15 files changed, 110 insertions(+), 1133 deletions(-) diff --git a/main/cloudfoundry_client/dropsonde/__init__.py b/main/cloudfoundry_client/dropsonde/__init__.py index e69de29..ef603c2 100644 --- a/main/cloudfoundry_client/dropsonde/__init__.py +++ b/main/cloudfoundry_client/dropsonde/__init__.py @@ -0,0 +1,3 @@ +import sys,os +# Ugly Hack needed see https://github.com/protocolbuffers/protobuf/issues/1491 +sys.path.append(os.path.dirname(__file__)) \ No newline at end of file diff --git a/main/cloudfoundry_client/dropsonde/envelope_pb2.py b/main/cloudfoundry_client/dropsonde/envelope_pb2.py index 2b55efb..f4c1211 100644 --- a/main/cloudfoundry_client/dropsonde/envelope_pb2.py +++ b/main/cloudfoundry_client/dropsonde/envelope_pb2.py @@ -1,263 +1,36 @@ +# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: envelope.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection +from google.protobuf import descriptor_pool as _descriptor_pool from google.protobuf import symbol_database as _symbol_database -from google.protobuf import descriptor_pb2 # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() -import cloudfoundry_client.dropsonde.http_pb2 as http__pb2 -import cloudfoundry_client.dropsonde.log_pb2 as log__pb2 -import cloudfoundry_client.dropsonde.metric_pb2 as metric__pb2 -import cloudfoundry_client.dropsonde.error_pb2 as error__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='envelope.proto', - package='cloudfoundry.dropsonde', - syntax='proto2', - serialized_pb=_b('\n\x0e\x65nvelope.proto\x12\x16\x63loudfoundry.dropsonde\x1a\nhttp.proto\x1a\tlog.proto\x1a\x0cmetric.proto\x1a\x0b\x65rror.proto\"\xde\x05\n\x08\x45nvelope\x12\x0e\n\x06origin\x18\x01 \x02(\t\x12=\n\teventType\x18\x02 \x02(\x0e\x32*.cloudfoundry.dropsonde.Envelope.EventType\x12\x11\n\ttimestamp\x18\x06 \x01(\x03\x12\x12\n\ndeployment\x18\r \x01(\t\x12\x0b\n\x03job\x18\x0e \x01(\t\x12\r\n\x05index\x18\x0f \x01(\t\x12\n\n\x02ip\x18\x10 \x01(\t\x12\x38\n\x04tags\x18\x11 \x03(\x0b\x32*.cloudfoundry.dropsonde.Envelope.TagsEntry\x12<\n\rhttpStartStop\x18\x07 \x01(\x0b\x32%.cloudfoundry.dropsonde.HttpStartStop\x12\x36\n\nlogMessage\x18\x08 \x01(\x0b\x32\".cloudfoundry.dropsonde.LogMessage\x12\x38\n\x0bvalueMetric\x18\t \x01(\x0b\x32#.cloudfoundry.dropsonde.ValueMetric\x12:\n\x0c\x63ounterEvent\x18\n \x01(\x0b\x32$.cloudfoundry.dropsonde.CounterEvent\x12,\n\x05\x65rror\x18\x0b \x01(\x0b\x32\x1d.cloudfoundry.dropsonde.Error\x12@\n\x0f\x63ontainerMetric\x18\x0c \x01(\x0b\x32\'.cloudfoundry.dropsonde.ContainerMetric\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"q\n\tEventType\x12\x11\n\rHttpStartStop\x10\x04\x12\x0e\n\nLogMessage\x10\x05\x12\x0f\n\x0bValueMetric\x10\x06\x12\x10\n\x0c\x43ounterEvent\x10\x07\x12\t\n\x05\x45rror\x10\x08\x12\x13\n\x0f\x43ontainerMetric\x10\tB1\n!org.cloudfoundry.dropsonde.eventsB\x0c\x45ventFactory') - , - dependencies=[http__pb2.DESCRIPTOR,log__pb2.DESCRIPTOR,metric__pb2.DESCRIPTOR,error__pb2.DESCRIPTOR,]) - - - -_ENVELOPE_EVENTTYPE = _descriptor.EnumDescriptor( - name='EventType', - full_name='cloudfoundry.dropsonde.Envelope.EventType', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='HttpStartStop', index=0, number=4, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='LogMessage', index=1, number=5, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ValueMetric', index=2, number=6, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CounterEvent', index=3, number=7, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='Error', index=4, number=8, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ContainerMetric', index=5, number=9, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=714, - serialized_end=827, -) -_sym_db.RegisterEnumDescriptor(_ENVELOPE_EVENTTYPE) - - -_ENVELOPE_TAGSENTRY = _descriptor.Descriptor( - name='TagsEntry', - full_name='cloudfoundry.dropsonde.Envelope.TagsEntry', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='cloudfoundry.dropsonde.Envelope.TagsEntry.key', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='cloudfoundry.dropsonde.Envelope.TagsEntry.value', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=_descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001')), - is_extendable=False, - syntax='proto2', - extension_ranges=[], - oneofs=[ - ], - serialized_start=669, - serialized_end=712, -) - -_ENVELOPE = _descriptor.Descriptor( - name='Envelope', - full_name='cloudfoundry.dropsonde.Envelope', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='origin', full_name='cloudfoundry.dropsonde.Envelope.origin', index=0, - number=1, type=9, cpp_type=9, label=2, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='eventType', full_name='cloudfoundry.dropsonde.Envelope.eventType', index=1, - number=2, type=14, cpp_type=8, label=2, - has_default_value=False, default_value=4, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='timestamp', full_name='cloudfoundry.dropsonde.Envelope.timestamp', index=2, - number=6, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='deployment', full_name='cloudfoundry.dropsonde.Envelope.deployment', index=3, - number=13, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='job', full_name='cloudfoundry.dropsonde.Envelope.job', index=4, - number=14, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='index', full_name='cloudfoundry.dropsonde.Envelope.index', index=5, - number=15, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='ip', full_name='cloudfoundry.dropsonde.Envelope.ip', index=6, - number=16, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tags', full_name='cloudfoundry.dropsonde.Envelope.tags', index=7, - number=17, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='httpStartStop', full_name='cloudfoundry.dropsonde.Envelope.httpStartStop', index=8, - number=7, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='logMessage', full_name='cloudfoundry.dropsonde.Envelope.logMessage', index=9, - number=8, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='valueMetric', full_name='cloudfoundry.dropsonde.Envelope.valueMetric', index=10, - number=9, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='counterEvent', full_name='cloudfoundry.dropsonde.Envelope.counterEvent', index=11, - number=10, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='error', full_name='cloudfoundry.dropsonde.Envelope.error', index=12, - number=11, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='containerMetric', full_name='cloudfoundry.dropsonde.Envelope.containerMetric', index=13, - number=12, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_ENVELOPE_TAGSENTRY, ], - enum_types=[ - _ENVELOPE_EVENTTYPE, - ], - options=None, - is_extendable=False, - syntax='proto2', - extension_ranges=[], - oneofs=[ - ], - serialized_start=93, - serialized_end=827, -) - -_ENVELOPE_TAGSENTRY.containing_type = _ENVELOPE -_ENVELOPE.fields_by_name['eventType'].enum_type = _ENVELOPE_EVENTTYPE -_ENVELOPE.fields_by_name['tags'].message_type = _ENVELOPE_TAGSENTRY -_ENVELOPE.fields_by_name['httpStartStop'].message_type = http__pb2._HTTPSTARTSTOP -_ENVELOPE.fields_by_name['logMessage'].message_type = log__pb2._LOGMESSAGE -_ENVELOPE.fields_by_name['valueMetric'].message_type = metric__pb2._VALUEMETRIC -_ENVELOPE.fields_by_name['counterEvent'].message_type = metric__pb2._COUNTEREVENT -_ENVELOPE.fields_by_name['error'].message_type = error__pb2._ERROR -_ENVELOPE.fields_by_name['containerMetric'].message_type = metric__pb2._CONTAINERMETRIC -_ENVELOPE_EVENTTYPE.containing_type = _ENVELOPE -DESCRIPTOR.message_types_by_name['Envelope'] = _ENVELOPE -_sym_db.RegisterFileDescriptor(DESCRIPTOR) +import http_pb2 as http__pb2 +import log_pb2 as log__pb2 +import metric_pb2 as metric__pb2 +import error_pb2 as error__pb2 -Envelope = _reflection.GeneratedProtocolMessageType('Envelope', (_message.Message,), dict( - TagsEntry = _reflection.GeneratedProtocolMessageType('TagsEntry', (_message.Message,), dict( - DESCRIPTOR = _ENVELOPE_TAGSENTRY, - __module__ = 'envelope_pb2' - # @@protoc_insertion_point(class_scope:cloudfoundry.dropsonde.Envelope.TagsEntry) - )) - , - DESCRIPTOR = _ENVELOPE, - __module__ = 'envelope_pb2' - # @@protoc_insertion_point(class_scope:cloudfoundry.dropsonde.Envelope) - )) -_sym_db.RegisterMessage(Envelope) -_sym_db.RegisterMessage(Envelope.TagsEntry) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0e\x65nvelope.proto\x12\x06\x65vents\x1a\nhttp.proto\x1a\tlog.proto\x1a\x0cmetric.proto\x1a\x0b\x65rror.proto\"\xde\x04\n\x08\x45nvelope\x12\x0e\n\x06origin\x18\x01 \x02(\t\x12-\n\teventType\x18\x02 \x02(\x0e\x32\x1a.events.Envelope.EventType\x12\x11\n\ttimestamp\x18\x06 \x01(\x03\x12\x12\n\ndeployment\x18\r \x01(\t\x12\x0b\n\x03job\x18\x0e \x01(\t\x12\r\n\x05index\x18\x0f \x01(\t\x12\n\n\x02ip\x18\x10 \x01(\t\x12(\n\x04tags\x18\x11 \x03(\x0b\x32\x1a.events.Envelope.TagsEntry\x12,\n\rhttpStartStop\x18\x07 \x01(\x0b\x32\x15.events.HttpStartStop\x12&\n\nlogMessage\x18\x08 \x01(\x0b\x32\x12.events.LogMessage\x12(\n\x0bvalueMetric\x18\t \x01(\x0b\x32\x13.events.ValueMetric\x12*\n\x0c\x63ounterEvent\x18\n \x01(\x0b\x32\x14.events.CounterEvent\x12\x1c\n\x05\x65rror\x18\x0b \x01(\x0b\x32\r.events.Error\x12\x30\n\x0f\x63ontainerMetric\x18\x0c \x01(\x0b\x32\x17.events.ContainerMetric\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"q\n\tEventType\x12\x11\n\rHttpStartStop\x10\x04\x12\x0e\n\nLogMessage\x10\x05\x12\x0f\n\x0bValueMetric\x10\x06\x12\x10\n\x0c\x43ounterEvent\x10\x07\x12\t\n\x05\x45rror\x10\x08\x12\x13\n\x0f\x43ontainerMetric\x10\tB1\n!org.cloudfoundry.dropsonde.eventsB\x0c\x45ventFactory') +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'envelope_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: -DESCRIPTOR.has_options = True -DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n!org.cloudfoundry.dropsonde.eventsB\014EventFactory')) -_ENVELOPE_TAGSENTRY.has_options = True -_ENVELOPE_TAGSENTRY._options = _descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001')) + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n!org.cloudfoundry.dropsonde.eventsB\014EventFactory' + _ENVELOPE_TAGSENTRY._options = None + _ENVELOPE_TAGSENTRY._serialized_options = b'8\001' + _ENVELOPE._serialized_start=77 + _ENVELOPE._serialized_end=683 + _ENVELOPE_TAGSENTRY._serialized_start=525 + _ENVELOPE_TAGSENTRY._serialized_end=568 + _ENVELOPE_EVENTTYPE._serialized_start=570 + _ENVELOPE_EVENTTYPE._serialized_end=683 # @@protoc_insertion_point(module_scope) diff --git a/main/cloudfoundry_client/dropsonde/error_pb2.py b/main/cloudfoundry_client/dropsonde/error_pb2.py index 571058d..68e59ff 100644 --- a/main/cloudfoundry_client/dropsonde/error_pb2.py +++ b/main/cloudfoundry_client/dropsonde/error_pb2.py @@ -1,13 +1,11 @@ +# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: error.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection +from google.protobuf import descriptor_pool as _descriptor_pool from google.protobuf import symbol_database as _symbol_database -from google.protobuf import descriptor_pb2 # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -15,71 +13,14 @@ -DESCRIPTOR = _descriptor.FileDescriptor( - name='error.proto', - package='cloudfoundry.dropsonde', - syntax='proto2', - serialized_pb=_b('\n\x0b\x65rror.proto\x12\x16\x63loudfoundry.dropsonde\"6\n\x05\x45rror\x12\x0e\n\x06source\x18\x01 \x02(\t\x12\x0c\n\x04\x63ode\x18\x02 \x02(\x05\x12\x0f\n\x07message\x18\x03 \x02(\tB1\n!org.cloudfoundry.dropsonde.eventsB\x0c\x45rrorFactory') -) - - - - -_ERROR = _descriptor.Descriptor( - name='Error', - full_name='cloudfoundry.dropsonde.Error', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='source', full_name='cloudfoundry.dropsonde.Error.source', index=0, - number=1, type=9, cpp_type=9, label=2, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='code', full_name='cloudfoundry.dropsonde.Error.code', index=1, - number=2, type=5, cpp_type=1, label=2, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='message', full_name='cloudfoundry.dropsonde.Error.message', index=2, - number=3, type=9, cpp_type=9, label=2, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto2', - extension_ranges=[], - oneofs=[ - ], - serialized_start=39, - serialized_end=93, -) - -DESCRIPTOR.message_types_by_name['Error'] = _ERROR -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -Error = _reflection.GeneratedProtocolMessageType('Error', (_message.Message,), dict( - DESCRIPTOR = _ERROR, - __module__ = 'error_pb2' - # @@protoc_insertion_point(class_scope:cloudfoundry.dropsonde.Error) - )) -_sym_db.RegisterMessage(Error) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0b\x65rror.proto\x12\x06\x65vents\"6\n\x05\x45rror\x12\x0e\n\x06source\x18\x01 \x02(\t\x12\x0c\n\x04\x63ode\x18\x02 \x02(\x05\x12\x0f\n\x07message\x18\x03 \x02(\tB1\n!org.cloudfoundry.dropsonde.eventsB\x0c\x45rrorFactory') +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'error_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: -DESCRIPTOR.has_options = True -DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n!org.cloudfoundry.dropsonde.eventsB\014ErrorFactory')) + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n!org.cloudfoundry.dropsonde.eventsB\014ErrorFactory' + _ERROR._serialized_start=23 + _ERROR._serialized_end=77 # @@protoc_insertion_point(module_scope) diff --git a/main/cloudfoundry_client/dropsonde/http_pb2.py b/main/cloudfoundry_client/dropsonde/http_pb2.py index 9cc50b8..1952842 100644 --- a/main/cloudfoundry_client/dropsonde/http_pb2.py +++ b/main/cloudfoundry_client/dropsonde/http_pb2.py @@ -1,431 +1,31 @@ +# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: http.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf.internal import enum_type_wrapper +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection +from google.protobuf import descriptor_pool as _descriptor_pool from google.protobuf import symbol_database as _symbol_database -from google.protobuf import descriptor_pb2 # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() -import cloudfoundry_client.dropsonde.uuid_pb2 as uuid__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='http.proto', - package='cloudfoundry.dropsonde', - syntax='proto2', - serialized_pb=_b('\n\nhttp.proto\x12\x16\x63loudfoundry.dropsonde\x1a\nuuid.proto\"\xa8\x03\n\rHttpStartStop\x12\x16\n\x0estartTimestamp\x18\x01 \x02(\x03\x12\x15\n\rstopTimestamp\x18\x02 \x02(\x03\x12/\n\trequestId\x18\x03 \x02(\x0b\x32\x1c.cloudfoundry.dropsonde.UUID\x12\x32\n\x08peerType\x18\x04 \x02(\x0e\x32 .cloudfoundry.dropsonde.PeerType\x12.\n\x06method\x18\x05 \x02(\x0e\x32\x1e.cloudfoundry.dropsonde.Method\x12\x0b\n\x03uri\x18\x06 \x02(\t\x12\x15\n\rremoteAddress\x18\x07 \x02(\t\x12\x11\n\tuserAgent\x18\x08 \x02(\t\x12\x12\n\nstatusCode\x18\t \x02(\x05\x12\x15\n\rcontentLength\x18\n \x02(\x03\x12\x33\n\rapplicationId\x18\x0c \x01(\x0b\x32\x1c.cloudfoundry.dropsonde.UUID\x12\x15\n\rinstanceIndex\x18\r \x01(\x05\x12\x12\n\ninstanceId\x18\x0e \x01(\t\x12\x11\n\tforwarded\x18\x0f \x03(\t*\"\n\x08PeerType\x12\n\n\x06\x43lient\x10\x01\x12\n\n\x06Server\x10\x02*\xc6\x04\n\x06Method\x12\x07\n\x03GET\x10\x01\x12\x08\n\x04POST\x10\x02\x12\x07\n\x03PUT\x10\x03\x12\n\n\x06\x44\x45LETE\x10\x04\x12\x08\n\x04HEAD\x10\x05\x12\x07\n\x03\x41\x43L\x10\x06\x12\x14\n\x10\x42\x41SELINE_CONTROL\x10\x07\x12\x08\n\x04\x42IND\x10\x08\x12\x0b\n\x07\x43HECKIN\x10\t\x12\x0c\n\x08\x43HECKOUT\x10\n\x12\x0b\n\x07\x43ONNECT\x10\x0b\x12\x08\n\x04\x43OPY\x10\x0c\x12\t\n\x05\x44\x45\x42UG\x10\r\x12\t\n\x05LABEL\x10\x0e\x12\x08\n\x04LINK\x10\x0f\x12\x08\n\x04LOCK\x10\x10\x12\t\n\x05MERGE\x10\x11\x12\x0e\n\nMKACTIVITY\x10\x12\x12\x0e\n\nMKCALENDAR\x10\x13\x12\t\n\x05MKCOL\x10\x14\x12\x11\n\rMKREDIRECTREF\x10\x15\x12\x0f\n\x0bMKWORKSPACE\x10\x16\x12\x08\n\x04MOVE\x10\x17\x12\x0b\n\x07OPTIONS\x10\x18\x12\x0e\n\nORDERPATCH\x10\x19\x12\t\n\x05PATCH\x10\x1a\x12\x07\n\x03PRI\x10\x1b\x12\x0c\n\x08PROPFIND\x10\x1c\x12\r\n\tPROPPATCH\x10\x1d\x12\n\n\x06REBIND\x10\x1e\x12\n\n\x06REPORT\x10\x1f\x12\n\n\x06SEARCH\x10 \x12\x0e\n\nSHOWMETHOD\x10!\x12\r\n\tSPACEJUMP\x10\"\x12\x0e\n\nTEXTSEARCH\x10#\x12\t\n\x05TRACE\x10$\x12\t\n\x05TRACK\x10%\x12\n\n\x06UNBIND\x10&\x12\x0e\n\nUNCHECKOUT\x10\'\x12\n\n\x06UNLINK\x10(\x12\n\n\x06UNLOCK\x10)\x12\n\n\x06UPDATE\x10*\x12\x15\n\x11UPDATEREDIRECTREF\x10+\x12\x13\n\x0fVERSION_CONTROL\x10,B0\n!org.cloudfoundry.dropsonde.eventsB\x0bHttpFactory') - , - dependencies=[uuid__pb2.DESCRIPTOR,]) - -_PEERTYPE = _descriptor.EnumDescriptor( - name='PeerType', - full_name='cloudfoundry.dropsonde.PeerType', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='Client', index=0, number=1, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='Server', index=1, number=2, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=477, - serialized_end=511, -) -_sym_db.RegisterEnumDescriptor(_PEERTYPE) - -PeerType = enum_type_wrapper.EnumTypeWrapper(_PEERTYPE) -_METHOD = _descriptor.EnumDescriptor( - name='Method', - full_name='cloudfoundry.dropsonde.Method', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='GET', index=0, number=1, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='POST', index=1, number=2, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='PUT', index=2, number=3, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='DELETE', index=3, number=4, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='HEAD', index=4, number=5, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ACL', index=5, number=6, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BASELINE_CONTROL', index=6, number=7, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BIND', index=7, number=8, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CHECKIN', index=8, number=9, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CHECKOUT', index=9, number=10, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CONNECT', index=10, number=11, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='COPY', index=11, number=12, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='DEBUG', index=12, number=13, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='LABEL', index=13, number=14, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='LINK', index=14, number=15, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='LOCK', index=15, number=16, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='MERGE', index=16, number=17, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='MKACTIVITY', index=17, number=18, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='MKCALENDAR', index=18, number=19, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='MKCOL', index=19, number=20, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='MKREDIRECTREF', index=20, number=21, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='MKWORKSPACE', index=21, number=22, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='MOVE', index=22, number=23, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='OPTIONS', index=23, number=24, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ORDERPATCH', index=24, number=25, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='PATCH', index=25, number=26, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='PRI', index=26, number=27, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='PROPFIND', index=27, number=28, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='PROPPATCH', index=28, number=29, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='REBIND', index=29, number=30, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='REPORT', index=30, number=31, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='SEARCH', index=31, number=32, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='SHOWMETHOD', index=32, number=33, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='SPACEJUMP', index=33, number=34, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='TEXTSEARCH', index=34, number=35, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='TRACE', index=35, number=36, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='TRACK', index=36, number=37, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UNBIND', index=37, number=38, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UNCHECKOUT', index=38, number=39, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UNLINK', index=39, number=40, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UNLOCK', index=40, number=41, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UPDATE', index=41, number=42, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UPDATEREDIRECTREF', index=42, number=43, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='VERSION_CONTROL', index=43, number=44, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=514, - serialized_end=1096, -) -_sym_db.RegisterEnumDescriptor(_METHOD) - -Method = enum_type_wrapper.EnumTypeWrapper(_METHOD) -Client = 1 -Server = 2 -GET = 1 -POST = 2 -PUT = 3 -DELETE = 4 -HEAD = 5 -ACL = 6 -BASELINE_CONTROL = 7 -BIND = 8 -CHECKIN = 9 -CHECKOUT = 10 -CONNECT = 11 -COPY = 12 -DEBUG = 13 -LABEL = 14 -LINK = 15 -LOCK = 16 -MERGE = 17 -MKACTIVITY = 18 -MKCALENDAR = 19 -MKCOL = 20 -MKREDIRECTREF = 21 -MKWORKSPACE = 22 -MOVE = 23 -OPTIONS = 24 -ORDERPATCH = 25 -PATCH = 26 -PRI = 27 -PROPFIND = 28 -PROPPATCH = 29 -REBIND = 30 -REPORT = 31 -SEARCH = 32 -SHOWMETHOD = 33 -SPACEJUMP = 34 -TEXTSEARCH = 35 -TRACE = 36 -TRACK = 37 -UNBIND = 38 -UNCHECKOUT = 39 -UNLINK = 40 -UNLOCK = 41 -UPDATE = 42 -UPDATEREDIRECTREF = 43 -VERSION_CONTROL = 44 - - - -_HTTPSTARTSTOP = _descriptor.Descriptor( - name='HttpStartStop', - full_name='cloudfoundry.dropsonde.HttpStartStop', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='startTimestamp', full_name='cloudfoundry.dropsonde.HttpStartStop.startTimestamp', index=0, - number=1, type=3, cpp_type=2, label=2, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='stopTimestamp', full_name='cloudfoundry.dropsonde.HttpStartStop.stopTimestamp', index=1, - number=2, type=3, cpp_type=2, label=2, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='requestId', full_name='cloudfoundry.dropsonde.HttpStartStop.requestId', index=2, - number=3, type=11, cpp_type=10, label=2, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='peerType', full_name='cloudfoundry.dropsonde.HttpStartStop.peerType', index=3, - number=4, type=14, cpp_type=8, label=2, - has_default_value=False, default_value=1, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='method', full_name='cloudfoundry.dropsonde.HttpStartStop.method', index=4, - number=5, type=14, cpp_type=8, label=2, - has_default_value=False, default_value=1, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='uri', full_name='cloudfoundry.dropsonde.HttpStartStop.uri', index=5, - number=6, type=9, cpp_type=9, label=2, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='remoteAddress', full_name='cloudfoundry.dropsonde.HttpStartStop.remoteAddress', index=6, - number=7, type=9, cpp_type=9, label=2, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='userAgent', full_name='cloudfoundry.dropsonde.HttpStartStop.userAgent', index=7, - number=8, type=9, cpp_type=9, label=2, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='statusCode', full_name='cloudfoundry.dropsonde.HttpStartStop.statusCode', index=8, - number=9, type=5, cpp_type=1, label=2, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='contentLength', full_name='cloudfoundry.dropsonde.HttpStartStop.contentLength', index=9, - number=10, type=3, cpp_type=2, label=2, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='applicationId', full_name='cloudfoundry.dropsonde.HttpStartStop.applicationId', index=10, - number=12, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='instanceIndex', full_name='cloudfoundry.dropsonde.HttpStartStop.instanceIndex', index=11, - number=13, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='instanceId', full_name='cloudfoundry.dropsonde.HttpStartStop.instanceId', index=12, - number=14, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='forwarded', full_name='cloudfoundry.dropsonde.HttpStartStop.forwarded', index=13, - number=15, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto2', - extension_ranges=[], - oneofs=[ - ], - serialized_start=51, - serialized_end=475, -) +import uuid_pb2 as uuid__pb2 -_HTTPSTARTSTOP.fields_by_name['requestId'].message_type = uuid__pb2._UUID -_HTTPSTARTSTOP.fields_by_name['peerType'].enum_type = _PEERTYPE -_HTTPSTARTSTOP.fields_by_name['method'].enum_type = _METHOD -_HTTPSTARTSTOP.fields_by_name['applicationId'].message_type = uuid__pb2._UUID -DESCRIPTOR.message_types_by_name['HttpStartStop'] = _HTTPSTARTSTOP -DESCRIPTOR.enum_types_by_name['PeerType'] = _PEERTYPE -DESCRIPTOR.enum_types_by_name['Method'] = _METHOD -_sym_db.RegisterFileDescriptor(DESCRIPTOR) -HttpStartStop = _reflection.GeneratedProtocolMessageType('HttpStartStop', (_message.Message,), dict( - DESCRIPTOR = _HTTPSTARTSTOP, - __module__ = 'http_pb2' - # @@protoc_insertion_point(class_scope:cloudfoundry.dropsonde.HttpStartStop) - )) -_sym_db.RegisterMessage(HttpStartStop) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nhttp.proto\x12\x06\x65vents\x1a\nuuid.proto\"\xe8\x02\n\rHttpStartStop\x12\x16\n\x0estartTimestamp\x18\x01 \x02(\x03\x12\x15\n\rstopTimestamp\x18\x02 \x02(\x03\x12\x1f\n\trequestId\x18\x03 \x02(\x0b\x32\x0c.events.UUID\x12\"\n\x08peerType\x18\x04 \x02(\x0e\x32\x10.events.PeerType\x12\x1e\n\x06method\x18\x05 \x02(\x0e\x32\x0e.events.Method\x12\x0b\n\x03uri\x18\x06 \x02(\t\x12\x15\n\rremoteAddress\x18\x07 \x02(\t\x12\x11\n\tuserAgent\x18\x08 \x02(\t\x12\x12\n\nstatusCode\x18\t \x02(\x05\x12\x15\n\rcontentLength\x18\n \x02(\x03\x12#\n\rapplicationId\x18\x0c \x01(\x0b\x32\x0c.events.UUID\x12\x15\n\rinstanceIndex\x18\r \x01(\x05\x12\x12\n\ninstanceId\x18\x0e \x01(\t\x12\x11\n\tforwarded\x18\x0f \x03(\t*\"\n\x08PeerType\x12\n\n\x06\x43lient\x10\x01\x12\n\n\x06Server\x10\x02*\xc6\x04\n\x06Method\x12\x07\n\x03GET\x10\x01\x12\x08\n\x04POST\x10\x02\x12\x07\n\x03PUT\x10\x03\x12\n\n\x06\x44\x45LETE\x10\x04\x12\x08\n\x04HEAD\x10\x05\x12\x07\n\x03\x41\x43L\x10\x06\x12\x14\n\x10\x42\x41SELINE_CONTROL\x10\x07\x12\x08\n\x04\x42IND\x10\x08\x12\x0b\n\x07\x43HECKIN\x10\t\x12\x0c\n\x08\x43HECKOUT\x10\n\x12\x0b\n\x07\x43ONNECT\x10\x0b\x12\x08\n\x04\x43OPY\x10\x0c\x12\t\n\x05\x44\x45\x42UG\x10\r\x12\t\n\x05LABEL\x10\x0e\x12\x08\n\x04LINK\x10\x0f\x12\x08\n\x04LOCK\x10\x10\x12\t\n\x05MERGE\x10\x11\x12\x0e\n\nMKACTIVITY\x10\x12\x12\x0e\n\nMKCALENDAR\x10\x13\x12\t\n\x05MKCOL\x10\x14\x12\x11\n\rMKREDIRECTREF\x10\x15\x12\x0f\n\x0bMKWORKSPACE\x10\x16\x12\x08\n\x04MOVE\x10\x17\x12\x0b\n\x07OPTIONS\x10\x18\x12\x0e\n\nORDERPATCH\x10\x19\x12\t\n\x05PATCH\x10\x1a\x12\x07\n\x03PRI\x10\x1b\x12\x0c\n\x08PROPFIND\x10\x1c\x12\r\n\tPROPPATCH\x10\x1d\x12\n\n\x06REBIND\x10\x1e\x12\n\n\x06REPORT\x10\x1f\x12\n\n\x06SEARCH\x10 \x12\x0e\n\nSHOWMETHOD\x10!\x12\r\n\tSPACEJUMP\x10\"\x12\x0e\n\nTEXTSEARCH\x10#\x12\t\n\x05TRACE\x10$\x12\t\n\x05TRACK\x10%\x12\n\n\x06UNBIND\x10&\x12\x0e\n\nUNCHECKOUT\x10\'\x12\n\n\x06UNLINK\x10(\x12\n\n\x06UNLOCK\x10)\x12\n\n\x06UPDATE\x10*\x12\x15\n\x11UPDATEREDIRECTREF\x10+\x12\x13\n\x0fVERSION_CONTROL\x10,B0\n!org.cloudfoundry.dropsonde.eventsB\x0bHttpFactory') +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'http_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: -DESCRIPTOR.has_options = True -DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n!org.cloudfoundry.dropsonde.eventsB\013HttpFactory')) + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n!org.cloudfoundry.dropsonde.eventsB\013HttpFactory' + _PEERTYPE._serialized_start=397 + _PEERTYPE._serialized_end=431 + _METHOD._serialized_start=434 + _METHOD._serialized_end=1016 + _HTTPSTARTSTOP._serialized_start=35 + _HTTPSTARTSTOP._serialized_end=395 # @@protoc_insertion_point(module_scope) diff --git a/main/cloudfoundry_client/dropsonde/log_pb2.py b/main/cloudfoundry_client/dropsonde/log_pb2.py index 371a196..da7f46d 100644 --- a/main/cloudfoundry_client/dropsonde/log_pb2.py +++ b/main/cloudfoundry_client/dropsonde/log_pb2.py @@ -1,13 +1,11 @@ +# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: log.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection +from google.protobuf import descriptor_pool as _descriptor_pool from google.protobuf import symbol_database as _symbol_database -from google.protobuf import descriptor_pb2 # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -15,117 +13,16 @@ -DESCRIPTOR = _descriptor.FileDescriptor( - name='log.proto', - package='cloudfoundry.dropsonde', - syntax='proto2', - serialized_pb=_b('\n\tlog.proto\x12\x16\x63loudfoundry.dropsonde\"\xd5\x01\n\nLogMessage\x12\x0f\n\x07message\x18\x01 \x02(\x0c\x12\x44\n\x0cmessage_type\x18\x02 \x02(\x0e\x32..cloudfoundry.dropsonde.LogMessage.MessageType\x12\x11\n\ttimestamp\x18\x03 \x02(\x03\x12\x0e\n\x06\x61pp_id\x18\x04 \x01(\t\x12\x13\n\x0bsource_type\x18\x05 \x01(\t\x12\x17\n\x0fsource_instance\x18\x06 \x01(\t\"\x1f\n\x0bMessageType\x12\x07\n\x03OUT\x10\x01\x12\x07\n\x03\x45RR\x10\x02\x42/\n!org.cloudfoundry.dropsonde.eventsB\nLogFactory') -) - - - -_LOGMESSAGE_MESSAGETYPE = _descriptor.EnumDescriptor( - name='MessageType', - full_name='cloudfoundry.dropsonde.LogMessage.MessageType', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='OUT', index=0, number=1, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ERR', index=1, number=2, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=220, - serialized_end=251, -) -_sym_db.RegisterEnumDescriptor(_LOGMESSAGE_MESSAGETYPE) - - -_LOGMESSAGE = _descriptor.Descriptor( - name='LogMessage', - full_name='cloudfoundry.dropsonde.LogMessage', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='message', full_name='cloudfoundry.dropsonde.LogMessage.message', index=0, - number=1, type=12, cpp_type=9, label=2, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='message_type', full_name='cloudfoundry.dropsonde.LogMessage.message_type', index=1, - number=2, type=14, cpp_type=8, label=2, - has_default_value=False, default_value=1, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='timestamp', full_name='cloudfoundry.dropsonde.LogMessage.timestamp', index=2, - number=3, type=3, cpp_type=2, label=2, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='app_id', full_name='cloudfoundry.dropsonde.LogMessage.app_id', index=3, - number=4, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='source_type', full_name='cloudfoundry.dropsonde.LogMessage.source_type', index=4, - number=5, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='source_instance', full_name='cloudfoundry.dropsonde.LogMessage.source_instance', index=5, - number=6, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - _LOGMESSAGE_MESSAGETYPE, - ], - options=None, - is_extendable=False, - syntax='proto2', - extension_ranges=[], - oneofs=[ - ], - serialized_start=38, - serialized_end=251, -) - -_LOGMESSAGE.fields_by_name['message_type'].enum_type = _LOGMESSAGE_MESSAGETYPE -_LOGMESSAGE_MESSAGETYPE.containing_type = _LOGMESSAGE -DESCRIPTOR.message_types_by_name['LogMessage'] = _LOGMESSAGE -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -LogMessage = _reflection.GeneratedProtocolMessageType('LogMessage', (_message.Message,), dict( - DESCRIPTOR = _LOGMESSAGE, - __module__ = 'log_pb2' - # @@protoc_insertion_point(class_scope:cloudfoundry.dropsonde.LogMessage) - )) -_sym_db.RegisterMessage(LogMessage) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\tlog.proto\x12\x06\x65vents\"\xc5\x01\n\nLogMessage\x12\x0f\n\x07message\x18\x01 \x02(\x0c\x12\x34\n\x0cmessage_type\x18\x02 \x02(\x0e\x32\x1e.events.LogMessage.MessageType\x12\x11\n\ttimestamp\x18\x03 \x02(\x03\x12\x0e\n\x06\x61pp_id\x18\x04 \x01(\t\x12\x13\n\x0bsource_type\x18\x05 \x01(\t\x12\x17\n\x0fsource_instance\x18\x06 \x01(\t\"\x1f\n\x0bMessageType\x12\x07\n\x03OUT\x10\x01\x12\x07\n\x03\x45RR\x10\x02\x42/\n!org.cloudfoundry.dropsonde.eventsB\nLogFactory') +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'log_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: -DESCRIPTOR.has_options = True -DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n!org.cloudfoundry.dropsonde.eventsB\nLogFactory')) + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n!org.cloudfoundry.dropsonde.eventsB\nLogFactory' + _LOGMESSAGE._serialized_start=22 + _LOGMESSAGE._serialized_end=219 + _LOGMESSAGE_MESSAGETYPE._serialized_start=188 + _LOGMESSAGE_MESSAGETYPE._serialized_end=219 # @@protoc_insertion_point(module_scope) diff --git a/main/cloudfoundry_client/dropsonde/metric_pb2.py b/main/cloudfoundry_client/dropsonde/metric_pb2.py index 76d7f11..3db344a 100644 --- a/main/cloudfoundry_client/dropsonde/metric_pb2.py +++ b/main/cloudfoundry_client/dropsonde/metric_pb2.py @@ -1,221 +1,30 @@ +# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: metric.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection +from google.protobuf import descriptor_pool as _descriptor_pool from google.protobuf import symbol_database as _symbol_database -from google.protobuf import descriptor_pb2 # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() -import cloudfoundry_client.dropsonde.uuid_pb2 as uuid__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='metric.proto', - package='cloudfoundry.dropsonde', - syntax='proto2', - serialized_pb=_b('\n\x0cmetric.proto\x12\x16\x63loudfoundry.dropsonde\x1a\nuuid.proto\"8\n\x0bValueMetric\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\r\n\x05value\x18\x02 \x02(\x01\x12\x0c\n\x04unit\x18\x03 \x02(\t\":\n\x0c\x43ounterEvent\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\r\n\x05\x64\x65lta\x18\x02 \x02(\x04\x12\r\n\x05total\x18\x03 \x01(\x04\"\xb0\x01\n\x0f\x43ontainerMetric\x12\x15\n\rapplicationId\x18\x01 \x02(\t\x12\x15\n\rinstanceIndex\x18\x02 \x02(\x05\x12\x15\n\rcpuPercentage\x18\x03 \x02(\x01\x12\x13\n\x0bmemoryBytes\x18\x04 \x02(\x04\x12\x11\n\tdiskBytes\x18\x05 \x02(\x04\x12\x18\n\x10memoryBytesQuota\x18\x06 \x01(\x04\x12\x16\n\x0e\x64iskBytesQuota\x18\x07 \x01(\x04\x42\x32\n!org.cloudfoundry.dropsonde.eventsB\rMetricFactory') - , - dependencies=[uuid__pb2.DESCRIPTOR,]) - - - - -_VALUEMETRIC = _descriptor.Descriptor( - name='ValueMetric', - full_name='cloudfoundry.dropsonde.ValueMetric', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='name', full_name='cloudfoundry.dropsonde.ValueMetric.name', index=0, - number=1, type=9, cpp_type=9, label=2, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='cloudfoundry.dropsonde.ValueMetric.value', index=1, - number=2, type=1, cpp_type=5, label=2, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='unit', full_name='cloudfoundry.dropsonde.ValueMetric.unit', index=2, - number=3, type=9, cpp_type=9, label=2, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto2', - extension_ranges=[], - oneofs=[ - ], - serialized_start=52, - serialized_end=108, -) - - -_COUNTEREVENT = _descriptor.Descriptor( - name='CounterEvent', - full_name='cloudfoundry.dropsonde.CounterEvent', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='name', full_name='cloudfoundry.dropsonde.CounterEvent.name', index=0, - number=1, type=9, cpp_type=9, label=2, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='delta', full_name='cloudfoundry.dropsonde.CounterEvent.delta', index=1, - number=2, type=4, cpp_type=4, label=2, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='total', full_name='cloudfoundry.dropsonde.CounterEvent.total', index=2, - number=3, type=4, cpp_type=4, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto2', - extension_ranges=[], - oneofs=[ - ], - serialized_start=110, - serialized_end=168, -) - - -_CONTAINERMETRIC = _descriptor.Descriptor( - name='ContainerMetric', - full_name='cloudfoundry.dropsonde.ContainerMetric', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='applicationId', full_name='cloudfoundry.dropsonde.ContainerMetric.applicationId', index=0, - number=1, type=9, cpp_type=9, label=2, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='instanceIndex', full_name='cloudfoundry.dropsonde.ContainerMetric.instanceIndex', index=1, - number=2, type=5, cpp_type=1, label=2, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='cpuPercentage', full_name='cloudfoundry.dropsonde.ContainerMetric.cpuPercentage', index=2, - number=3, type=1, cpp_type=5, label=2, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='memoryBytes', full_name='cloudfoundry.dropsonde.ContainerMetric.memoryBytes', index=3, - number=4, type=4, cpp_type=4, label=2, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='diskBytes', full_name='cloudfoundry.dropsonde.ContainerMetric.diskBytes', index=4, - number=5, type=4, cpp_type=4, label=2, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='memoryBytesQuota', full_name='cloudfoundry.dropsonde.ContainerMetric.memoryBytesQuota', index=5, - number=6, type=4, cpp_type=4, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='diskBytesQuota', full_name='cloudfoundry.dropsonde.ContainerMetric.diskBytesQuota', index=6, - number=7, type=4, cpp_type=4, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto2', - extension_ranges=[], - oneofs=[ - ], - serialized_start=171, - serialized_end=347, -) - -DESCRIPTOR.message_types_by_name['ValueMetric'] = _VALUEMETRIC -DESCRIPTOR.message_types_by_name['CounterEvent'] = _COUNTEREVENT -DESCRIPTOR.message_types_by_name['ContainerMetric'] = _CONTAINERMETRIC -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -ValueMetric = _reflection.GeneratedProtocolMessageType('ValueMetric', (_message.Message,), dict( - DESCRIPTOR = _VALUEMETRIC, - __module__ = 'metric_pb2' - # @@protoc_insertion_point(class_scope:cloudfoundry.dropsonde.ValueMetric) - )) -_sym_db.RegisterMessage(ValueMetric) -CounterEvent = _reflection.GeneratedProtocolMessageType('CounterEvent', (_message.Message,), dict( - DESCRIPTOR = _COUNTEREVENT, - __module__ = 'metric_pb2' - # @@protoc_insertion_point(class_scope:cloudfoundry.dropsonde.CounterEvent) - )) -_sym_db.RegisterMessage(CounterEvent) -ContainerMetric = _reflection.GeneratedProtocolMessageType('ContainerMetric', (_message.Message,), dict( - DESCRIPTOR = _CONTAINERMETRIC, - __module__ = 'metric_pb2' - # @@protoc_insertion_point(class_scope:cloudfoundry.dropsonde.ContainerMetric) - )) -_sym_db.RegisterMessage(ContainerMetric) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cmetric.proto\x12\x06\x65vents\"8\n\x0bValueMetric\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\r\n\x05value\x18\x02 \x02(\x01\x12\x0c\n\x04unit\x18\x03 \x02(\t\":\n\x0c\x43ounterEvent\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\r\n\x05\x64\x65lta\x18\x02 \x02(\x04\x12\r\n\x05total\x18\x03 \x01(\x04\"\xb0\x01\n\x0f\x43ontainerMetric\x12\x15\n\rapplicationId\x18\x01 \x02(\t\x12\x15\n\rinstanceIndex\x18\x02 \x02(\x05\x12\x15\n\rcpuPercentage\x18\x03 \x02(\x01\x12\x13\n\x0bmemoryBytes\x18\x04 \x02(\x04\x12\x11\n\tdiskBytes\x18\x05 \x02(\x04\x12\x18\n\x10memoryBytesQuota\x18\x06 \x01(\x04\x12\x16\n\x0e\x64iskBytesQuota\x18\x07 \x01(\x04\x42\x32\n!org.cloudfoundry.dropsonde.eventsB\rMetricFactory') +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'metric_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: -DESCRIPTOR.has_options = True -DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n!org.cloudfoundry.dropsonde.eventsB\rMetricFactory')) + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n!org.cloudfoundry.dropsonde.eventsB\rMetricFactory' + _VALUEMETRIC._serialized_start=24 + _VALUEMETRIC._serialized_end=80 + _COUNTEREVENT._serialized_start=82 + _COUNTEREVENT._serialized_end=140 + _CONTAINERMETRIC._serialized_start=143 + _CONTAINERMETRIC._serialized_end=319 # @@protoc_insertion_point(module_scope) diff --git a/main/cloudfoundry_client/dropsonde/uuid_pb2.py b/main/cloudfoundry_client/dropsonde/uuid_pb2.py index 547aa21..f4d5d4a 100644 --- a/main/cloudfoundry_client/dropsonde/uuid_pb2.py +++ b/main/cloudfoundry_client/dropsonde/uuid_pb2.py @@ -1,13 +1,11 @@ +# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: uuid.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection +from google.protobuf import descriptor_pool as _descriptor_pool from google.protobuf import symbol_database as _symbol_database -from google.protobuf import descriptor_pb2 # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -15,64 +13,14 @@ -DESCRIPTOR = _descriptor.FileDescriptor( - name='uuid.proto', - package='cloudfoundry.dropsonde', - syntax='proto2', - serialized_pb=_b('\n\nuuid.proto\x12\x16\x63loudfoundry.dropsonde\"!\n\x04UUID\x12\x0b\n\x03low\x18\x01 \x02(\x04\x12\x0c\n\x04high\x18\x02 \x02(\x04\x42\x30\n!org.cloudfoundry.dropsonde.eventsB\x0bUuidFactory') -) - - - - -_UUID = _descriptor.Descriptor( - name='UUID', - full_name='cloudfoundry.dropsonde.UUID', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='low', full_name='cloudfoundry.dropsonde.UUID.low', index=0, - number=1, type=4, cpp_type=4, label=2, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='high', full_name='cloudfoundry.dropsonde.UUID.high', index=1, - number=2, type=4, cpp_type=4, label=2, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto2', - extension_ranges=[], - oneofs=[ - ], - serialized_start=38, - serialized_end=71, -) - -DESCRIPTOR.message_types_by_name['UUID'] = _UUID -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -UUID = _reflection.GeneratedProtocolMessageType('UUID', (_message.Message,), dict( - DESCRIPTOR = _UUID, - __module__ = 'uuid_pb2' - # @@protoc_insertion_point(class_scope:cloudfoundry.dropsonde.UUID) - )) -_sym_db.RegisterMessage(UUID) +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nuuid.proto\x12\x06\x65vents\"!\n\x04UUID\x12\x0b\n\x03low\x18\x01 \x02(\x04\x12\x0c\n\x04high\x18\x02 \x02(\x04\x42\x30\n!org.cloudfoundry.dropsonde.eventsB\x0bUuidFactory') +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'uuid_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: -DESCRIPTOR.has_options = True -DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n!org.cloudfoundry.dropsonde.eventsB\013UuidFactory')) + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n!org.cloudfoundry.dropsonde.eventsB\013UuidFactory' + _UUID._serialized_start=22 + _UUID._serialized_end=55 # @@protoc_insertion_point(module_scope) diff --git a/requirements.txt b/requirements.txt index 7ce708e..9ffa6fa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ aiohttp>=3.8.0 -protobuf>=3.6.1, < 3.19.0 +protobuf>=3.20.0, < 5.0.0dev oauth2-client==1.2.1 websocket-client==0.59.0 PyYAML==6.0 diff --git a/setup.py b/setup.py index 938199b..ead241e 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,8 @@ src_dir = "main" package_directory = "cloudfoundry_client" package_name = "cloudfoundry-client" -loggregator_dir = "loggregator" +dropsonde_src = "vendors/dropsonde-protocol" +dropsonde_dest = "dropsonde" sys.path.insert(0, os.path.realpath(src_dir)) version_file = "%s/%s/__init__.py" % (src_dir, package_directory) @@ -41,17 +42,18 @@ def finalize_options(self): pass def run(self): - source_path = os.path.join(os.path.dirname(__file__), src_dir, package_directory, loggregator_dir) + source_path = os.path.join(os.path.dirname(__file__), dropsonde_src) for file_protobuf in os.listdir(source_path): if file_protobuf.endswith(".proto"): file_path = os.path.join(source_path, file_protobuf) - print("Generating %s from %s" % (file_protobuf, file_path)) + dest_path = os.path.join(src_dir, package_directory, dropsonde_dest) + print("Generating %s from %s to %s" % (file_protobuf, file_path, dest_path)) subprocess.call( [ "protoc", - "-I", + "--proto_path", source_path, - "--python_out=%s" % source_path, + "--python_out=%s" % dest_path, file_path, ] ) diff --git a/vendors/dropsonde-protocol/envelope.proto b/vendors/dropsonde-protocol/envelope.proto index 024ee38..4cdcd3c 100644 --- a/vendors/dropsonde-protocol/envelope.proto +++ b/vendors/dropsonde-protocol/envelope.proto @@ -1,3 +1,4 @@ +syntax = "proto2"; package events; option java_package = "org.cloudfoundry.dropsonde.events"; option java_outer_classname = "EventFactory"; diff --git a/vendors/dropsonde-protocol/error.proto b/vendors/dropsonde-protocol/error.proto index 9ba2baf..fa13a0f 100644 --- a/vendors/dropsonde-protocol/error.proto +++ b/vendors/dropsonde-protocol/error.proto @@ -1,3 +1,4 @@ +syntax = "proto2"; package events; option java_package = "org.cloudfoundry.dropsonde.events"; diff --git a/vendors/dropsonde-protocol/http.proto b/vendors/dropsonde-protocol/http.proto index 2e9c9b4..d6b0a0b 100644 --- a/vendors/dropsonde-protocol/http.proto +++ b/vendors/dropsonde-protocol/http.proto @@ -1,3 +1,4 @@ +syntax = "proto2"; package events; option java_package = "org.cloudfoundry.dropsonde.events"; diff --git a/vendors/dropsonde-protocol/log.proto b/vendors/dropsonde-protocol/log.proto index c012f87..0882792 100644 --- a/vendors/dropsonde-protocol/log.proto +++ b/vendors/dropsonde-protocol/log.proto @@ -1,3 +1,4 @@ +syntax = "proto2"; package events; option java_package = "org.cloudfoundry.dropsonde.events"; diff --git a/vendors/dropsonde-protocol/metric.proto b/vendors/dropsonde-protocol/metric.proto index 006344d..1d1fce0 100644 --- a/vendors/dropsonde-protocol/metric.proto +++ b/vendors/dropsonde-protocol/metric.proto @@ -1,10 +1,9 @@ +syntax = "proto2"; package events; option java_package = "org.cloudfoundry.dropsonde.events"; option java_outer_classname = "MetricFactory"; -import "uuid.proto"; - /// A ValueMetric indicates the value of a metric at an instant in time. message ValueMetric { required string name = 1; /// Name of the metric. Must be consistent for downstream consumers to associate events semantically. diff --git a/vendors/dropsonde-protocol/uuid.proto b/vendors/dropsonde-protocol/uuid.proto index 44c1c5a..826a51e 100644 --- a/vendors/dropsonde-protocol/uuid.proto +++ b/vendors/dropsonde-protocol/uuid.proto @@ -1,3 +1,4 @@ +syntax = "proto2"; package events; option java_package = "org.cloudfoundry.dropsonde.events"; From cb8e521088b752acdf8dec3debc193b569df863f Mon Sep 17 00:00:00 2001 From: Marc-Philip Werner Date: Fri, 24 Feb 2023 09:33:13 +0100 Subject: [PATCH 144/264] bump websocket-client to 1.3.1 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 9ffa6fa..e775402 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ aiohttp>=3.8.0 protobuf>=3.20.0, < 5.0.0dev oauth2-client==1.2.1 -websocket-client==0.59.0 +websocket-client~=1.3.1 PyYAML==6.0 requests>=2.5.0 polling2==0.5.0 From 902934ff6e5329cbbd3e4b0196acb5d335de610a Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Tue, 14 Feb 2023 15:44:52 +0100 Subject: [PATCH 145/264] Prepare 1.33.0 version --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 6233636..a985dc1 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.31.0" +__version__ = "1.33.0" From 6b86c6456038de625151821112d2b33fe832c71b Mon Sep 17 00:00:00 2001 From: d059372 Date: Fri, 14 Apr 2023 13:28:06 +0200 Subject: [PATCH 146/264] Bump oauth2-client to v1.4.0 - Adopt tests to not check authorization header --- requirements.txt | 2 +- test/test_client.py | 24 +++++++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/requirements.txt b/requirements.txt index e775402..4856a4e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ aiohttp>=3.8.0 protobuf>=3.20.0, < 5.0.0dev -oauth2-client==1.2.1 +oauth2-client==1.4.0 websocket-client~=1.3.1 PyYAML==6.0 requests>=2.5.0 diff --git a/test/test_client.py b/test/test_client.py index b53d6de..2591a47 100644 --- a/test/test_client.py +++ b/test/test_client.py @@ -60,8 +60,15 @@ def test_grant_password_request_with_token_format_opaque(self): self.assertEqual("Bearer access-token", session.headers.get("Authorization")) requests.post.assert_called_with( requests.post.return_value.url, - data=dict(grant_type="password", username="somebody", scope="", password="p@s$w0rd", token_format="opaque"), - headers=dict(Accept="application/json", Authorization="Basic Y2Y6"), + data=dict( + grant_type="password", + username="somebody", + scope="", + password="p@s$w0rd", + token_format="opaque", + client_id="cf", + ), + headers=dict(Accept="application/json"), proxies=dict(http="", https=""), verify=True, ) @@ -84,8 +91,10 @@ def test_refresh_request_with_token_format_opaque(self): self.assertEqual("Bearer access-token", session.headers.get("Authorization")) requests.post.assert_called_with( requests.post.return_value.url, - data=dict(grant_type="refresh_token", scope="", refresh_token="refresh-token", token_format="opaque"), - headers=dict(Accept="application/json", Authorization="Basic Y2Y6"), + data=dict( + grant_type="refresh_token", scope="", refresh_token="refresh-token", token_format="opaque", client_id="cf" + ), + headers=dict(Accept="application/json"), proxies=dict(http="", https=""), verify=True, ) @@ -112,8 +121,8 @@ def test_refresh_request_with_token_from_cf_config(self): self.assertEqual("Bearer access-token", session.headers.get("Authorization")) requests.post.assert_called_with( requests.post.return_value.url, - data=dict(grant_type="refresh_token", scope="", refresh_token="refresh-token"), - headers=dict(Accept="application/json", Authorization="Basic Y2Y6"), + data=dict(grant_type="refresh_token", scope="", refresh_token="refresh-token", client_id="cf"), + headers=dict(Accept="application/json"), proxies=proxy, verify=True, ) @@ -144,8 +153,9 @@ def test_grant_password_request_with_login_hint(self): scope="", password="p@s$w0rd", login_hint="%7B%22origin%22%3A%22uaa%22%7D", + client_id="cf", ), - headers=dict(Accept="application/json", Authorization="Basic Y2Y6"), + headers=dict(Accept="application/json"), proxies=dict(http="", https=""), verify=True, ) From 1caf21fdae2e36ccb33fa6a60585b76eb1173560 Mon Sep 17 00:00:00 2001 From: d059372 Date: Fri, 14 Apr 2023 13:33:17 +0200 Subject: [PATCH 147/264] Add `user_agent` parameter Allows to set/override the http user agent in oauth2-client --- main/cloudfoundry_client/client.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 8521373..7216dbb 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -138,6 +138,7 @@ def __init__(self, target_endpoint: str, client_id: str = "cf", client_secret: s The passed string has to be a URL-Encoded JSON Object, containing the field origin with value as origin_key of an identity provider. Note that this identity provider must support the grant type password. See UAA API specifications + :param user_agent: string. Can be used to set a custom http user agent """ proxy = kwargs.get("proxy", dict(http="", https="")) verify = kwargs.get("verify", True) @@ -150,7 +151,7 @@ def __init__(self, target_endpoint: str, client_id: str = "cf", client_secret: s service_information = ServiceInformation( None, "%s/oauth/token" % info.authorization_endpoint, client_id, client_secret, [], verify ) - super(CloudFoundryClient, self).__init__(service_information, proxies=proxy) + super(CloudFoundryClient, self).__init__(service_information, proxies=proxy, user_agent=kwargs.get("user_agent")) self.v2 = V2(target_endpoint_trimmed, self) self.v3 = V3(target_endpoint_trimmed, self) self._doppler = ( From 15927d68e8c0ef91ee22a553aa227d145b6f854a Mon Sep 17 00:00:00 2001 From: d059372 Date: Fri, 14 Apr 2023 14:46:29 +0200 Subject: [PATCH 148/264] Set default user agent --- main/cloudfoundry_client/client.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/client.py b/main/cloudfoundry_client/client.py index 7216dbb..77cc556 100644 --- a/main/cloudfoundry_client/client.py +++ b/main/cloudfoundry_client/client.py @@ -151,7 +151,11 @@ def __init__(self, target_endpoint: str, client_id: str = "cf", client_secret: s service_information = ServiceInformation( None, "%s/oauth/token" % info.authorization_endpoint, client_id, client_secret, [], verify ) - super(CloudFoundryClient, self).__init__(service_information, proxies=proxy, user_agent=kwargs.get("user_agent")) + super(CloudFoundryClient, self).__init__( + service_information, + proxies=proxy, + user_agent=kwargs.get("user_agent", "cf-python-client") + ) self.v2 = V2(target_endpoint_trimmed, self) self.v3 = V3(target_endpoint_trimmed, self) self._doppler = ( From 0005f41f540f61ad0336dc1f700dfeed9c2d8653 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Fri, 14 Apr 2023 14:52:52 +0200 Subject: [PATCH 149/264] Prepare 1.34.0 version --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index a985dc1..0c8bfae 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.33.0" +__version__ = "1.34.0" From 7a7a4a13a5b5f7e25ae85d9f6171f0a46bb40d5c Mon Sep 17 00:00:00 2001 From: Jochen Ehret Date: Tue, 18 Apr 2023 13:55:58 +0200 Subject: [PATCH 150/264] Update websocket-client to 1.5.1 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4856a4e..e331daa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ aiohttp>=3.8.0 protobuf>=3.20.0, < 5.0.0dev oauth2-client==1.4.0 -websocket-client~=1.3.1 +websocket-client~=1.5.1 PyYAML==6.0 requests>=2.5.0 polling2==0.5.0 From 693707da7a40407222ec0e9c59364247fddf2efe Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Tue, 18 Apr 2023 17:44:31 +0200 Subject: [PATCH 151/264] Prepare 1.34.1 release --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 0c8bfae..0ebf790 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.34.0" +__version__ = "1.34.1" From 6e0e8b60d3cd5b756c7c953c13691670d44efaec Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Fri, 28 Apr 2023 14:59:21 +0200 Subject: [PATCH 152/264] Use new version of oauth client verion 1.4.0 of oauth client was bugged and needs to be upgraded to 1.4.2 Closes #191 --- requirements.txt | 2 +- test/test_client.py | 24 +++++++----------------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/requirements.txt b/requirements.txt index e331daa..ef29655 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ aiohttp>=3.8.0 protobuf>=3.20.0, < 5.0.0dev -oauth2-client==1.4.0 +oauth2-client==1.4.2 websocket-client~=1.5.1 PyYAML==6.0 requests>=2.5.0 diff --git a/test/test_client.py b/test/test_client.py index 2591a47..b53d6de 100644 --- a/test/test_client.py +++ b/test/test_client.py @@ -60,15 +60,8 @@ def test_grant_password_request_with_token_format_opaque(self): self.assertEqual("Bearer access-token", session.headers.get("Authorization")) requests.post.assert_called_with( requests.post.return_value.url, - data=dict( - grant_type="password", - username="somebody", - scope="", - password="p@s$w0rd", - token_format="opaque", - client_id="cf", - ), - headers=dict(Accept="application/json"), + data=dict(grant_type="password", username="somebody", scope="", password="p@s$w0rd", token_format="opaque"), + headers=dict(Accept="application/json", Authorization="Basic Y2Y6"), proxies=dict(http="", https=""), verify=True, ) @@ -91,10 +84,8 @@ def test_refresh_request_with_token_format_opaque(self): self.assertEqual("Bearer access-token", session.headers.get("Authorization")) requests.post.assert_called_with( requests.post.return_value.url, - data=dict( - grant_type="refresh_token", scope="", refresh_token="refresh-token", token_format="opaque", client_id="cf" - ), - headers=dict(Accept="application/json"), + data=dict(grant_type="refresh_token", scope="", refresh_token="refresh-token", token_format="opaque"), + headers=dict(Accept="application/json", Authorization="Basic Y2Y6"), proxies=dict(http="", https=""), verify=True, ) @@ -121,8 +112,8 @@ def test_refresh_request_with_token_from_cf_config(self): self.assertEqual("Bearer access-token", session.headers.get("Authorization")) requests.post.assert_called_with( requests.post.return_value.url, - data=dict(grant_type="refresh_token", scope="", refresh_token="refresh-token", client_id="cf"), - headers=dict(Accept="application/json"), + data=dict(grant_type="refresh_token", scope="", refresh_token="refresh-token"), + headers=dict(Accept="application/json", Authorization="Basic Y2Y6"), proxies=proxy, verify=True, ) @@ -153,9 +144,8 @@ def test_grant_password_request_with_login_hint(self): scope="", password="p@s$w0rd", login_hint="%7B%22origin%22%3A%22uaa%22%7D", - client_id="cf", ), - headers=dict(Accept="application/json"), + headers=dict(Accept="application/json", Authorization="Basic Y2Y6"), proxies=dict(http="", https=""), verify=True, ) From 7a9ddf1adbfcafab0deb4cec20ead346917d13f0 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Fri, 28 Apr 2023 15:09:40 +0200 Subject: [PATCH 153/264] Prepare 1.34.2 release --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 0ebf790..9c8ddb4 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.34.1" +__version__ = "1.34.2" From 962b1f14b2cbb0c57a44ce755da659c644db9be4 Mon Sep 17 00:00:00 2001 From: Brian Pandola Date: Mon, 15 May 2023 13:32:07 -0700 Subject: [PATCH 154/264] Add support for `GET /v3/service_instances/:guid/permissions` --- main/cloudfoundry_client/v3/service_instances.py | 6 ++++++ .../GET_{id}_permissions_response.json | 4 ++++ test/v3/test_service_instances.py | 15 +++++++++++++++ 3 files changed, 25 insertions(+) create mode 100644 test/fixtures/v3/service_instances/GET_{id}_permissions_response.json diff --git a/main/cloudfoundry_client/v3/service_instances.py b/main/cloudfoundry_client/v3/service_instances.py index 70a3d14..ad33614 100644 --- a/main/cloudfoundry_client/v3/service_instances.py +++ b/main/cloudfoundry_client/v3/service_instances.py @@ -1,5 +1,6 @@ from typing import Optional, TYPE_CHECKING, List +from cloudfoundry_client.common_objects import JsonObject from cloudfoundry_client.v3.entities import Entity, EntityManager, ToOneRelationship if TYPE_CHECKING: @@ -72,3 +73,8 @@ def update( def remove(self, guid: str, asynchronous: bool = True): super(ServiceInstanceManager, self)._remove(guid, asynchronous) + + def get_permissions(self, instance_guid: str) -> JsonObject: + return super(ServiceInstanceManager, self)._get( + "%s%s/%s/permissions" % (self.target_endpoint, self.entity_uri, instance_guid) + ) diff --git a/test/fixtures/v3/service_instances/GET_{id}_permissions_response.json b/test/fixtures/v3/service_instances/GET_{id}_permissions_response.json new file mode 100644 index 0000000..d7001ba --- /dev/null +++ b/test/fixtures/v3/service_instances/GET_{id}_permissions_response.json @@ -0,0 +1,4 @@ +{ + "read": true, + "manage": false +} \ No newline at end of file diff --git a/test/v3/test_service_instances.py b/test/v3/test_service_instances.py index f2a23b2..4a08a0c 100644 --- a/test/v3/test_service_instances.py +++ b/test/v3/test_service_instances.py @@ -3,6 +3,7 @@ from unittest.mock import patch from abstract_test_case import AbstractTestCase +from cloudfoundry_client.common_objects import JsonObject from cloudfoundry_client.v3.entities import Entity @@ -157,3 +158,17 @@ def test_remove_synchronous(self, sleepmock): self.client.v3.service_instances.remove("service_instance_id", asynchronous=False) self.client.delete.assert_called_with(self.client.delete.return_value.url) assert self.client.get.call_count == 2 + + def test_get_permissions(self): + self.client.get.return_value = self.mock_response( + "/v3/service_instances/service_instance_id/permissions", + HTTPStatus.OK, + None, + "v3", + "service_instances", + "GET_{id}_permissions_response.json" + ) + permissions = self.client.v3.service_instances.get_permissions("service_instance_id") + self.assertIsInstance(permissions, JsonObject) + self.assertTrue(permissions["read"]) + self.assertFalse(permissions["manage"]) From f51d9eee684b9085bcd4600c668bc3b1be827e75 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Tue, 16 May 2023 09:28:23 +0200 Subject: [PATCH 155/264] Prepare version 1.35.0 --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 9c8ddb4..d5a1ed4 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.34.2" +__version__ = "1.35.0" From 2deb6aef7d056eef17eb1c6d30f59a225ba98c88 Mon Sep 17 00:00:00 2001 From: d059372 Date: Thu, 13 Jul 2023 14:18:24 +0200 Subject: [PATCH 156/264] Bump websocket-client to v1.6.1 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ef29655..d0ed315 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ aiohttp>=3.8.0 protobuf>=3.20.0, < 5.0.0dev oauth2-client==1.4.2 -websocket-client~=1.5.1 +websocket-client~=1.6.1 PyYAML==6.0 requests>=2.5.0 polling2==0.5.0 From 709b5dc5b4e175d6c6c60ee057c8ebe8475fc212 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Thu, 13 Jul 2023 15:14:00 +0200 Subject: [PATCH 157/264] Prepare version 1.35.1 --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index d5a1ed4..802be72 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.35.0" +__version__ = "1.35.1" From dee04ccb50f04a1135a3555aebba0967bf939aa9 Mon Sep 17 00:00:00 2001 From: Yusuf <47318342+yusufsheiqh@users.noreply.github.com> Date: Tue, 18 Jul 2023 09:44:12 +0100 Subject: [PATCH 158/264] Add inclusive ordered comparison clause for `PyYAML` package --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d0ed315..42c6e6a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,6 +2,6 @@ aiohttp>=3.8.0 protobuf>=3.20.0, < 5.0.0dev oauth2-client==1.4.2 websocket-client~=1.6.1 -PyYAML==6.0 +PyYAML>=6.0 requests>=2.5.0 polling2==0.5.0 From 2e281b94bdb4e600f500ac179b79853564d83140 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Wed, 19 Jul 2023 11:51:49 +0200 Subject: [PATCH 159/264] Prepare version 1.35.2 --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index 802be72..fdd62e3 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.35.1" +__version__ = "1.35.2" From 2f713f122a35a8fde58caae1dbfc0190d54a14ca Mon Sep 17 00:00:00 2001 From: Brian Pandola Date: Tue, 2 Jan 2024 17:52:14 -0800 Subject: [PATCH 160/264] Update deprecated `assertEquals` calls This method is not available in Python >= 3.12 --- test/operations/push/test_push.py | 2 +- test/v3/test_apps.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/operations/push/test_push.py b/test/operations/push/test_push.py index 2e70337..15cef3a 100644 --- a/test/operations/push/test_push.py +++ b/test/operations/push/test_push.py @@ -41,7 +41,7 @@ def test_split_route_without_path(self): def test_to_host_should_remove_unwanted_characters(self): host = PushOperation._to_host("idzone-3.0.7-rec-tb1_bobby") - self.assertEquals("idzone-307-rec-tb1-bobby", host) + self.assertEqual("idzone-307-rec-tb1-bobby", host) @patch.object( sys, diff --git a/test/v3/test_apps.py b/test/v3/test_apps.py index 1130e18..c601170 100644 --- a/test/v3/test_apps.py +++ b/test/v3/test_apps.py @@ -81,7 +81,7 @@ def test_restart(self): app = self.client.v3.apps.restart("app_id") self.assertIsInstance(app, JsonObject) - self.assertEquals("my_app", app["name"]) + self.assertEqual("my_app", app["name"]) def test_remove(self): self.client.delete.return_value = self.mock_response("/v3/apps/app_id", HTTPStatus.NO_CONTENT, None) @@ -94,7 +94,7 @@ def test_get_env(self): ) env = self.client.v3.apps.get_env("app_id") self.assertIsInstance(env, JsonObject) - self.assertEquals(env["application_env_json"]["VCAP_APPLICATION"]["limits"]["fds"], 16384) + self.assertEqual(env["application_env_json"]["VCAP_APPLICATION"]["limits"]["fds"], 16384) def test_get_routes(self): self.client.get.return_value = self.mock_response( @@ -102,7 +102,7 @@ def test_get_routes(self): ) routes = self.client.v3.apps.get_routes("app_id") self.assertIsInstance(routes, JsonObject) - self.assertEquals(routes["resources"][0]["destinations"][0]["guid"], "385bf117-17f5-4689-8c5c-08c6cc821fed") + self.assertEqual(routes["resources"][0]["destinations"][0]["guid"], "385bf117-17f5-4689-8c5c-08c6cc821fed") def test_get_include_space_and_org(self): self.client.get.return_value = self.mock_response( From 2d0da9824e388e89254a677e2f7ef9c3cec30f1a Mon Sep 17 00:00:00 2001 From: Brian Pandola Date: Tue, 2 Jan 2024 17:54:01 -0800 Subject: [PATCH 161/264] Update Supported Python Versions *Drop support for Python 3.7 *Add support for Python 3.11 and 3.12 *Update package classifiers *Update README.rst --- .github/workflows/pythonpackage.yml | 2 +- README.rst | 1 + dev-requirements.txt | 1 + setup.py | 4 +++- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 9b668c5..2bd0802 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -9,7 +9,7 @@ jobs: strategy: max-parallel: 1 matrix: - python-version: ["3.7", "3.8", "3.9", "3.10"] + python-version: ["3.8", "3.9", "3.10","3.11","3.12"] steps: - uses: actions/checkout@v1 diff --git a/README.rst b/README.rst index 1865fd1..f8f1474 100644 --- a/README.rst +++ b/README.rst @@ -17,6 +17,7 @@ Supported versions - Starting version ``1.11.0``, versions older than python ``3.6.0`` will not be supported anymore. This late version was released by the end 2016. For those that are still using python 2.7, it won't be supported by the end of 2020 and all library shall stop supporting it. - Starting version ``1.25.0``, versions older than python ``3.7.0`` will not be supported anymore. +- Starting version ``1.36.0``, versions older than python ``3.8.0`` will not be supported anymore. See `official documentation`_. diff --git a/dev-requirements.txt b/dev-requirements.txt index 0889174..ca66374 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,2 +1,3 @@ black==22.12.0 flake8==5.0.4 +setuptools; python_version >= '3.12' \ No newline at end of file diff --git a/setup.py b/setup.py index ead241e..ddcfa0a 100644 --- a/setup.py +++ b/setup.py @@ -69,15 +69,17 @@ def run(self): description="A client library for CloudFoundry", long_description=open("README.rst").read(), url="http://github.com/antechrestos/cf-python-client", + python_requires=">=3.8", classifiers=[ "Programming Language :: Python", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Topic :: Communications", ], entry_points={ From 8668e46384f1c54b9d38864678a3e27564888b3c Mon Sep 17 00:00:00 2001 From: Brian Pandola Date: Tue, 2 Jan 2024 17:55:53 -0800 Subject: [PATCH 162/264] Formatting --- .github/workflows/pythonpackage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 2bd0802..e8838f7 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -9,7 +9,7 @@ jobs: strategy: max-parallel: 1 matrix: - python-version: ["3.8", "3.9", "3.10","3.11","3.12"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v1 From 888b0b1ff3955c830e84ac9f4bed76caf63a925e Mon Sep 17 00:00:00 2001 From: Brian Pandola Date: Tue, 2 Jan 2024 18:08:17 -0800 Subject: [PATCH 163/264] Create dependabot.yml --- .github/dependabot.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..806c04f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,15 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "monthly" From 514e13ff23005e3a0292ad3d601d3712f9b6fd6f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 08:22:05 +0000 Subject: [PATCH 164/264] Bump actions/setup-python from 1 to 5 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 1 to 5. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v1...v5) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/pythonpackage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index e8838f7..1fe230a 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -14,7 +14,7 @@ jobs: steps: - uses: actions/checkout@v1 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies From 86ee123ba4fc7f2193882ae7363b9e710998d7e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 08:22:09 +0000 Subject: [PATCH 165/264] Bump actions/checkout from 1 to 4 Bumps [actions/checkout](https://github.com/actions/checkout) from 1 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v1...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/pythonpackage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index e8838f7..d32e90e 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -12,7 +12,7 @@ jobs: python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: From 57c7d249bd5241973a9189718cf4829e5f5bd761 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 08:22:49 +0000 Subject: [PATCH 166/264] Update websocket-client requirement from ~=1.6.1 to ~=1.7.0 Updates the requirements on [websocket-client](https://github.com/websocket-client/websocket-client) to permit the latest version. - [Release notes](https://github.com/websocket-client/websocket-client/releases) - [Changelog](https://github.com/websocket-client/websocket-client/blob/master/ChangeLog) - [Commits](https://github.com/websocket-client/websocket-client/compare/v1.6.1...v1.7.0) --- updated-dependencies: - dependency-name: websocket-client dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 42c6e6a..12db4d2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ aiohttp>=3.8.0 protobuf>=3.20.0, < 5.0.0dev oauth2-client==1.4.2 -websocket-client~=1.6.1 +websocket-client~=1.7.0 PyYAML>=6.0 requests>=2.5.0 polling2==0.5.0 From 1e9a5dfa5fca1d5f6be4ffa0d747e7d060040a01 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 08:23:15 +0000 Subject: [PATCH 167/264] Bump black from 22.12.0 to 23.12.1 Bumps [black](https://github.com/psf/black) from 22.12.0 to 23.12.1. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/22.12.0...23.12.1) --- updated-dependencies: - dependency-name: black dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- dev-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index ca66374..b07827f 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,3 +1,3 @@ -black==22.12.0 +black==23.12.1 flake8==5.0.4 setuptools; python_version >= '3.12' \ No newline at end of file From 43ae32a26bfdd05295c3a53106379c4a5c6e2139 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 08:45:48 +0000 Subject: [PATCH 168/264] Bump flake8 from 5.0.4 to 6.1.0 Bumps [flake8](https://github.com/pycqa/flake8) from 5.0.4 to 6.1.0. - [Commits](https://github.com/pycqa/flake8/compare/5.0.4...6.1.0) --- updated-dependencies: - dependency-name: flake8 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- dev-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index b07827f..3aa7631 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,3 +1,3 @@ black==23.12.1 -flake8==5.0.4 +flake8==6.1.0 setuptools; python_version >= '3.12' \ No newline at end of file From daf073b496ca13900d4f726d22348cb539b09e00 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Wed, 3 Jan 2024 14:04:52 +0100 Subject: [PATCH 169/264] chore(code) remove type comparison using type native function --- main/cloudfoundry_client/errors.py | 2 +- main/cloudfoundry_client/main/main.py | 2 +- .../operations/push/validation/manifest.py | 15 +++++++++------ main/cloudfoundry_client/v2/apps.py | 4 ++-- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/main/cloudfoundry_client/errors.py b/main/cloudfoundry_client/errors.py index 888fe28..684db73 100644 --- a/main/cloudfoundry_client/errors.py +++ b/main/cloudfoundry_client/errors.py @@ -14,7 +14,7 @@ def __init__(self, status_code: HTTPStatus, body, request_id=None): def __str__(self): error_message = self.status_code.name - if type(self.body) == str: + if isinstance(self.body, str): error_message += f" = {self.body}" else: error_message += f" = {json.dumps(self.body)}" diff --git a/main/cloudfoundry_client/main/main.py b/main/cloudfoundry_client/main/main.py index bed8a33..e8027b0 100644 --- a/main/cloudfoundry_client/main/main.py +++ b/main/cloudfoundry_client/main/main.py @@ -110,7 +110,7 @@ def build_client_from_configuration(previous_configuration: dict = None) -> Clou client.init_with_token(configuration["refresh_token"]) return client except Exception as ex: - if type(ex) == ConnectionError: + if isinstance(ex, ConnectionError): raise else: _logger.exception("Could not restore configuration. Cleaning and recreating") diff --git a/main/cloudfoundry_client/operations/push/validation/manifest.py b/main/cloudfoundry_client/operations/push/validation/manifest.py index 326bfcf..6272e36 100644 --- a/main/cloudfoundry_client/operations/push/validation/manifest.py +++ b/main/cloudfoundry_client/operations/push/validation/manifest.py @@ -90,12 +90,12 @@ def _convert_positive(manifest: dict, field: str): def _convert_boolean(manifest: dict, field: str): if field in manifest: field_value = manifest[field] - manifest[field] = field_value if type(field_value) == bool else field_value.lower() == "true" + manifest[field] = field_value if isinstance(field_value, bool) else field_value.lower() == "true" @staticmethod def _validate_routes(manifest: dict): for route in manifest.get("routes", []): - if type(route) != dict or "route" not in route: + if not (isinstance(route, dict)) or "route" not in route: raise AssertionError("routes attribute must be a list of object containing a route attribute") @staticmethod @@ -125,11 +125,14 @@ def _absolute_path(manifest_directory: str, manifest: dict): def _convert_environment(app_manifest: dict): environment = app_manifest.get("env") if environment is not None: - if type(environment) != dict: + if not (isinstance(environment, dict)): raise AssertionError("'env' entry must be a dictionary") app_manifest["env"] = { - key: json.dumps(value) for key, value in environment.items() if value is not None and type(value) != str + key: json.dumps(value) for key, value in environment.items() + if value is not None and not (isinstance(value, str)) + } + app_manifest["env"].update({ + key: value for key, value in environment.items() + if value is not None and isinstance(value, str) } - app_manifest["env"].update( - {key: value for key, value in environment.items() if value is not None and type(value) == str} ) diff --git a/main/cloudfoundry_client/v2/apps.py b/main/cloudfoundry_client/v2/apps.py index 0ffe16f..cf68ed8 100644 --- a/main/cloudfoundry_client/v2/apps.py +++ b/main/cloudfoundry_client/v2/apps.py @@ -195,11 +195,11 @@ def _wait_for_instances_in_state( sleep(check_time) sum_waiting += check_time - def _safe_get_instances(self, application_guid: str) -> JsonObject: + def _safe_get_instances(self, application_guid: str) -> Dict[str, JsonObject]: try: return self.get_instances(application_guid) except InvalidStatusCode as ex: - if ex.status_code == HTTPStatus.BAD_REQUEST and type(ex.body) == dict: + if ex.status_code == HTTPStatus.BAD_REQUEST and isinstance(ex.body, dict): code = ex.body.get("code", -1) # 170002: staging not finished # 220001: instances error From fa4b480d485be7ee55e5c0fc78e9adb43907f0b1 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Wed, 17 Jan 2024 08:52:59 +0100 Subject: [PATCH 170/264] Prepare version 1.36.0 --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index fdd62e3..a0f14ee 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.35.2" +__version__ = "1.36.0" From cdc7ad049d534e51a7249d16159d42c5f06ee39d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 18:17:01 +0000 Subject: [PATCH 171/264] Bump flake8 from 6.1.0 to 7.0.0 Bumps [flake8](https://github.com/pycqa/flake8) from 6.1.0 to 7.0.0. - [Commits](https://github.com/pycqa/flake8/compare/6.1.0...7.0.0) --- updated-dependencies: - dependency-name: flake8 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- dev-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 3aa7631..34593ad 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,3 +1,3 @@ black==23.12.1 -flake8==6.1.0 +flake8==7.0.0 setuptools; python_version >= '3.12' \ No newline at end of file From a5e14d453c83aecff014aea6730564b00019e3e2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 18:17:10 +0000 Subject: [PATCH 172/264] Update protobuf requirement from <5.0.0dev,>=3.20.0 to >=3.20.0,<6.0.0 Updates the requirements on [protobuf](https://github.com/protocolbuffers/protobuf) to permit the latest version. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Changelog](https://github.com/protocolbuffers/protobuf/blob/main/protobuf_release.bzl) - [Commits](https://github.com/protocolbuffers/protobuf/compare/v3.20.0...v5.26.0-rc1) --- updated-dependencies: - dependency-name: protobuf dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 12db4d2..f51ac1b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ aiohttp>=3.8.0 -protobuf>=3.20.0, < 5.0.0dev +protobuf>= 3.20.0, < 6.0.0 oauth2-client==1.4.2 websocket-client~=1.7.0 PyYAML>=6.0 From f928e3018d3fb2732b6b93d57e87458e234befac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 19:02:56 +0000 Subject: [PATCH 173/264] Update websocket-client requirement from ~=1.7.0 to ~=1.8.0 Updates the requirements on [websocket-client](https://github.com/websocket-client/websocket-client) to permit the latest version. - [Release notes](https://github.com/websocket-client/websocket-client/releases) - [Changelog](https://github.com/websocket-client/websocket-client/blob/master/ChangeLog) - [Commits](https://github.com/websocket-client/websocket-client/compare/v1.7.0...v1.8.0) --- updated-dependencies: - dependency-name: websocket-client dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 12db4d2..a96fd56 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ aiohttp>=3.8.0 protobuf>=3.20.0, < 5.0.0dev oauth2-client==1.4.2 -websocket-client~=1.7.0 +websocket-client~=1.8.0 PyYAML>=6.0 requests>=2.5.0 polling2==0.5.0 From 821d699186721fccd689f47641903d8010d500c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 May 2024 14:01:32 +0000 Subject: [PATCH 174/264] Bump black from 23.12.1 to 24.4.2 Bumps [black](https://github.com/psf/black) from 23.12.1 to 24.4.2. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/23.12.1...24.4.2) --- updated-dependencies: - dependency-name: black dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- dev-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 34593ad..a5c10bc 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,3 +1,3 @@ -black==23.12.1 +black==24.4.2 flake8==7.0.0 setuptools; python_version >= '3.12' \ No newline at end of file From 3db5572c1e8d66e02fe24c55240cf0c18c9c37c1 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Thu, 2 May 2024 16:07:51 +0200 Subject: [PATCH 175/264] Prepare version 1.37.0 --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index a0f14ee..a1f2423 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.36.0" +__version__ = "1.37.0" From ebb5177e8cae70836997d5bf7077dab14dc3dba9 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Fri, 3 May 2024 09:18:57 +0200 Subject: [PATCH 176/264] fixes(push) fixes host resolution fixes #211 --- .../operations/push/push.py | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/main/cloudfoundry_client/operations/push/push.py b/main/cloudfoundry_client/operations/push/push.py index d4dd0c3..c894bd1 100644 --- a/main/cloudfoundry_client/operations/push/push.py +++ b/main/cloudfoundry_client/operations/push/push.py @@ -152,20 +152,18 @@ def _build_new_requested_routes( _logger.error("Neither path nor port provided for route", requested_route) raise AssertionError("Cannot set both port and path for route: %s" % requested_route) host, domain_name, domain = PushOperation._resolve_domain(route, private_domains, shared_domains) - if port is not None and host is not None: - _logger.error("Host provided in route %s for tcp domain %s", requested_route, domain_name) - raise AssertionError( - "For route (%s) refers to domain %s that is a tcp one. It is hence routed by port and not by host" - % (requested_route, domain_name) - ) route_to_map = None - if port is not None and domain["entity"].get("router_group_type") != "tcp": - _logger.error("Port provided in route %s for non tcp domain %s", requested_route, domain_name) - raise AssertionError("Cannot set port on route(%s) for non tcp domain" % requested_route) - elif domain["entity"].get("router_group_type") == "tcp" and port is None: - _logger.error("No port provided in route %s for tcp domain %s", requested_route, domain_name) - raise AssertionError("Please specify a port on route (%s) for tcp domain" % requested_route) - elif domain["entity"].get("router_group_type") == "tcp": + if port is not None: + if domain["entity"].get("router_group_type") != "tcp": + _logger.error("Port provided in route %s for non tcp domain %s", requested_route, domain_name) + raise AssertionError("Cannot set port on route(%s) for non tcp domain" % requested_route) + elif len(host) > 0: + _logger.error("Host provided in route %s for tcp domain %s", requested_route, domain_name) + raise AssertionError( + "For route (%s) refers to domain %s that is a tcp one. " + "It is hence routed by port and not by host" + % (requested_route, domain_name) + ) if not any( [route["entity"]["domain_guid"] == domain["metadata"]["guid"] and route["entity"]["port"] == port] for route in existing_routes From 2ce7bb60934332620f008a1626950f437c8ebb1c Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Fri, 3 May 2024 09:26:13 +0200 Subject: [PATCH 177/264] fixes(ci) improve workflow trigger --- .github/workflows/pythonpackage.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 2434630..f676ae3 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -1,10 +1,14 @@ name: Python package -on: [push, pull_request] +on: + workflow_dispatch: + pull_request: + push: + branches: + - main jobs: build: - runs-on: ubuntu-latest strategy: max-parallel: 1 @@ -13,16 +17,20 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt pip install -r dev-requirements.txt + - name: Launch tests run: python setup.py test + - name: Launch Linting run: flake8 --show-source --statistics From d3b6bb5883e66bf6843492be86e7c918e5101a0b Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Fri, 3 May 2024 09:30:56 +0200 Subject: [PATCH 178/264] Prepare version 1.37.1 --- main/cloudfoundry_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/cloudfoundry_client/__init__.py b/main/cloudfoundry_client/__init__.py index a1f2423..5740c67 100644 --- a/main/cloudfoundry_client/__init__.py +++ b/main/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.37.0" +__version__ = "1.37.1" From 4716dfb314736d2f50c4b1b87d9536ab533977bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 18:34:18 +0000 Subject: [PATCH 179/264] Bump flake8 from 7.0.0 to 7.1.0 Bumps [flake8](https://github.com/pycqa/flake8) from 7.0.0 to 7.1.0. - [Commits](https://github.com/pycqa/flake8/compare/7.0.0...7.1.0) --- updated-dependencies: - dependency-name: flake8 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- dev-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index a5c10bc..cfbd04a 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,3 +1,3 @@ black==24.4.2 -flake8==7.0.0 +flake8==7.1.0 setuptools; python_version >= '3.12' \ No newline at end of file From 5f7de4c23b202ba7f6cd27e9ddae39a25a32772c Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Fri, 12 Jul 2024 13:25:01 +0200 Subject: [PATCH 180/264] fixes remove main directory and pyt package directly in repository root --- {main/cloudfoundry_client => cloudfoundry_client}/__init__.py | 0 {main/cloudfoundry_client => cloudfoundry_client}/client.py | 0 .../cloudfoundry_client => cloudfoundry_client}/common_objects.py | 0 .../doppler/__init__.py | 0 .../cloudfoundry_client => cloudfoundry_client}/doppler/client.py | 0 .../doppler/websocket_envelope_reader.py | 0 .../dropsonde/__init__.py | 0 .../dropsonde/envelope_pb2.py | 0 .../dropsonde/error_pb2.py | 0 .../dropsonde/http_pb2.py | 0 .../dropsonde/log_pb2.py | 0 .../dropsonde/metric_pb2.py | 0 .../dropsonde/uuid_pb2.py | 0 {main/cloudfoundry_client => cloudfoundry_client}/errors.py | 0 .../cloudfoundry_client => cloudfoundry_client}/main/__init__.py | 0 .../main/apps_command_domain.py | 0 .../main/command_domain.py | 0 {main/cloudfoundry_client => cloudfoundry_client}/main/main.py | 0 .../main/operation_commands.py | 0 .../main/tasks_command_domain.py | 0 .../networking/__init__.py | 0 .../networking/entities.py | 0 .../networking/v1/__init__.py | 0 .../networking/v1/external/__init__.py | 0 .../networking/v1/external/policies.py | 0 .../networking/v1/external/tags.py | 0 .../operations/__init__.py | 0 .../operations/push/__init__.py | 0 .../operations/push/cf_ignore.py | 0 .../operations/push/file_helper.py | 0 .../operations/push/push.py | 0 .../operations/push/validation/__init__.py | 0 .../operations/push/validation/manifest.py | 0 .../rlpgateway/__init__.py | 0 .../rlpgateway/client.py | 0 {main/cloudfoundry_client => cloudfoundry_client}/v2/__init__.py | 0 {main/cloudfoundry_client => cloudfoundry_client}/v2/apps.py | 0 .../cloudfoundry_client => cloudfoundry_client}/v2/buildpacks.py | 0 {main/cloudfoundry_client => cloudfoundry_client}/v2/entities.py | 0 {main/cloudfoundry_client => cloudfoundry_client}/v2/events.py | 0 {main/cloudfoundry_client => cloudfoundry_client}/v2/jobs.py | 0 {main/cloudfoundry_client => cloudfoundry_client}/v2/resources.py | 0 {main/cloudfoundry_client => cloudfoundry_client}/v2/routes.py | 0 .../v2/service_bindings.py | 0 .../v2/service_brokers.py | 0 .../v2/service_instances.py | 0 .../v2/service_keys.py | 0 .../v2/service_plan_visibilities.py | 0 .../v2/service_plans.py | 0 {main/cloudfoundry_client => cloudfoundry_client}/v2/spaces.py | 0 {main/cloudfoundry_client => cloudfoundry_client}/v3/__init__.py | 0 {main/cloudfoundry_client => cloudfoundry_client}/v3/apps.py | 0 .../cloudfoundry_client => cloudfoundry_client}/v3/buildpacks.py | 0 {main/cloudfoundry_client => cloudfoundry_client}/v3/domains.py | 0 {main/cloudfoundry_client => cloudfoundry_client}/v3/entities.py | 0 .../v3/feature_flags.py | 0 .../v3/isolation_segments.py | 0 {main/cloudfoundry_client => cloudfoundry_client}/v3/jobs.py | 0 .../v3/organization_quotas.py | 0 .../v3/organizations.py | 0 {main/cloudfoundry_client => cloudfoundry_client}/v3/processes.py | 0 {main/cloudfoundry_client => cloudfoundry_client}/v3/roles.py | 0 .../v3/security_groups.py | 0 .../v3/service_brokers.py | 0 .../v3/service_credential_bindings.py | 0 .../v3/service_instances.py | 0 .../v3/service_offerings.py | 0 .../v3/service_plans.py | 0 {main/cloudfoundry_client => cloudfoundry_client}/v3/spaces.py | 0 {main/cloudfoundry_client => cloudfoundry_client}/v3/tasks.py | 0 70 files changed, 0 insertions(+), 0 deletions(-) rename {main/cloudfoundry_client => cloudfoundry_client}/__init__.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/client.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/common_objects.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/doppler/__init__.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/doppler/client.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/doppler/websocket_envelope_reader.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/dropsonde/__init__.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/dropsonde/envelope_pb2.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/dropsonde/error_pb2.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/dropsonde/http_pb2.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/dropsonde/log_pb2.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/dropsonde/metric_pb2.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/dropsonde/uuid_pb2.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/errors.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/main/__init__.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/main/apps_command_domain.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/main/command_domain.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/main/main.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/main/operation_commands.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/main/tasks_command_domain.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/networking/__init__.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/networking/entities.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/networking/v1/__init__.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/networking/v1/external/__init__.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/networking/v1/external/policies.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/networking/v1/external/tags.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/operations/__init__.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/operations/push/__init__.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/operations/push/cf_ignore.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/operations/push/file_helper.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/operations/push/push.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/operations/push/validation/__init__.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/operations/push/validation/manifest.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/rlpgateway/__init__.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/rlpgateway/client.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v2/__init__.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v2/apps.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v2/buildpacks.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v2/entities.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v2/events.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v2/jobs.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v2/resources.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v2/routes.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v2/service_bindings.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v2/service_brokers.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v2/service_instances.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v2/service_keys.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v2/service_plan_visibilities.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v2/service_plans.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v2/spaces.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v3/__init__.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v3/apps.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v3/buildpacks.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v3/domains.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v3/entities.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v3/feature_flags.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v3/isolation_segments.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v3/jobs.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v3/organization_quotas.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v3/organizations.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v3/processes.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v3/roles.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v3/security_groups.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v3/service_brokers.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v3/service_credential_bindings.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v3/service_instances.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v3/service_offerings.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v3/service_plans.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v3/spaces.py (100%) rename {main/cloudfoundry_client => cloudfoundry_client}/v3/tasks.py (100%) diff --git a/main/cloudfoundry_client/__init__.py b/cloudfoundry_client/__init__.py similarity index 100% rename from main/cloudfoundry_client/__init__.py rename to cloudfoundry_client/__init__.py diff --git a/main/cloudfoundry_client/client.py b/cloudfoundry_client/client.py similarity index 100% rename from main/cloudfoundry_client/client.py rename to cloudfoundry_client/client.py diff --git a/main/cloudfoundry_client/common_objects.py b/cloudfoundry_client/common_objects.py similarity index 100% rename from main/cloudfoundry_client/common_objects.py rename to cloudfoundry_client/common_objects.py diff --git a/main/cloudfoundry_client/doppler/__init__.py b/cloudfoundry_client/doppler/__init__.py similarity index 100% rename from main/cloudfoundry_client/doppler/__init__.py rename to cloudfoundry_client/doppler/__init__.py diff --git a/main/cloudfoundry_client/doppler/client.py b/cloudfoundry_client/doppler/client.py similarity index 100% rename from main/cloudfoundry_client/doppler/client.py rename to cloudfoundry_client/doppler/client.py diff --git a/main/cloudfoundry_client/doppler/websocket_envelope_reader.py b/cloudfoundry_client/doppler/websocket_envelope_reader.py similarity index 100% rename from main/cloudfoundry_client/doppler/websocket_envelope_reader.py rename to cloudfoundry_client/doppler/websocket_envelope_reader.py diff --git a/main/cloudfoundry_client/dropsonde/__init__.py b/cloudfoundry_client/dropsonde/__init__.py similarity index 100% rename from main/cloudfoundry_client/dropsonde/__init__.py rename to cloudfoundry_client/dropsonde/__init__.py diff --git a/main/cloudfoundry_client/dropsonde/envelope_pb2.py b/cloudfoundry_client/dropsonde/envelope_pb2.py similarity index 100% rename from main/cloudfoundry_client/dropsonde/envelope_pb2.py rename to cloudfoundry_client/dropsonde/envelope_pb2.py diff --git a/main/cloudfoundry_client/dropsonde/error_pb2.py b/cloudfoundry_client/dropsonde/error_pb2.py similarity index 100% rename from main/cloudfoundry_client/dropsonde/error_pb2.py rename to cloudfoundry_client/dropsonde/error_pb2.py diff --git a/main/cloudfoundry_client/dropsonde/http_pb2.py b/cloudfoundry_client/dropsonde/http_pb2.py similarity index 100% rename from main/cloudfoundry_client/dropsonde/http_pb2.py rename to cloudfoundry_client/dropsonde/http_pb2.py diff --git a/main/cloudfoundry_client/dropsonde/log_pb2.py b/cloudfoundry_client/dropsonde/log_pb2.py similarity index 100% rename from main/cloudfoundry_client/dropsonde/log_pb2.py rename to cloudfoundry_client/dropsonde/log_pb2.py diff --git a/main/cloudfoundry_client/dropsonde/metric_pb2.py b/cloudfoundry_client/dropsonde/metric_pb2.py similarity index 100% rename from main/cloudfoundry_client/dropsonde/metric_pb2.py rename to cloudfoundry_client/dropsonde/metric_pb2.py diff --git a/main/cloudfoundry_client/dropsonde/uuid_pb2.py b/cloudfoundry_client/dropsonde/uuid_pb2.py similarity index 100% rename from main/cloudfoundry_client/dropsonde/uuid_pb2.py rename to cloudfoundry_client/dropsonde/uuid_pb2.py diff --git a/main/cloudfoundry_client/errors.py b/cloudfoundry_client/errors.py similarity index 100% rename from main/cloudfoundry_client/errors.py rename to cloudfoundry_client/errors.py diff --git a/main/cloudfoundry_client/main/__init__.py b/cloudfoundry_client/main/__init__.py similarity index 100% rename from main/cloudfoundry_client/main/__init__.py rename to cloudfoundry_client/main/__init__.py diff --git a/main/cloudfoundry_client/main/apps_command_domain.py b/cloudfoundry_client/main/apps_command_domain.py similarity index 100% rename from main/cloudfoundry_client/main/apps_command_domain.py rename to cloudfoundry_client/main/apps_command_domain.py diff --git a/main/cloudfoundry_client/main/command_domain.py b/cloudfoundry_client/main/command_domain.py similarity index 100% rename from main/cloudfoundry_client/main/command_domain.py rename to cloudfoundry_client/main/command_domain.py diff --git a/main/cloudfoundry_client/main/main.py b/cloudfoundry_client/main/main.py similarity index 100% rename from main/cloudfoundry_client/main/main.py rename to cloudfoundry_client/main/main.py diff --git a/main/cloudfoundry_client/main/operation_commands.py b/cloudfoundry_client/main/operation_commands.py similarity index 100% rename from main/cloudfoundry_client/main/operation_commands.py rename to cloudfoundry_client/main/operation_commands.py diff --git a/main/cloudfoundry_client/main/tasks_command_domain.py b/cloudfoundry_client/main/tasks_command_domain.py similarity index 100% rename from main/cloudfoundry_client/main/tasks_command_domain.py rename to cloudfoundry_client/main/tasks_command_domain.py diff --git a/main/cloudfoundry_client/networking/__init__.py b/cloudfoundry_client/networking/__init__.py similarity index 100% rename from main/cloudfoundry_client/networking/__init__.py rename to cloudfoundry_client/networking/__init__.py diff --git a/main/cloudfoundry_client/networking/entities.py b/cloudfoundry_client/networking/entities.py similarity index 100% rename from main/cloudfoundry_client/networking/entities.py rename to cloudfoundry_client/networking/entities.py diff --git a/main/cloudfoundry_client/networking/v1/__init__.py b/cloudfoundry_client/networking/v1/__init__.py similarity index 100% rename from main/cloudfoundry_client/networking/v1/__init__.py rename to cloudfoundry_client/networking/v1/__init__.py diff --git a/main/cloudfoundry_client/networking/v1/external/__init__.py b/cloudfoundry_client/networking/v1/external/__init__.py similarity index 100% rename from main/cloudfoundry_client/networking/v1/external/__init__.py rename to cloudfoundry_client/networking/v1/external/__init__.py diff --git a/main/cloudfoundry_client/networking/v1/external/policies.py b/cloudfoundry_client/networking/v1/external/policies.py similarity index 100% rename from main/cloudfoundry_client/networking/v1/external/policies.py rename to cloudfoundry_client/networking/v1/external/policies.py diff --git a/main/cloudfoundry_client/networking/v1/external/tags.py b/cloudfoundry_client/networking/v1/external/tags.py similarity index 100% rename from main/cloudfoundry_client/networking/v1/external/tags.py rename to cloudfoundry_client/networking/v1/external/tags.py diff --git a/main/cloudfoundry_client/operations/__init__.py b/cloudfoundry_client/operations/__init__.py similarity index 100% rename from main/cloudfoundry_client/operations/__init__.py rename to cloudfoundry_client/operations/__init__.py diff --git a/main/cloudfoundry_client/operations/push/__init__.py b/cloudfoundry_client/operations/push/__init__.py similarity index 100% rename from main/cloudfoundry_client/operations/push/__init__.py rename to cloudfoundry_client/operations/push/__init__.py diff --git a/main/cloudfoundry_client/operations/push/cf_ignore.py b/cloudfoundry_client/operations/push/cf_ignore.py similarity index 100% rename from main/cloudfoundry_client/operations/push/cf_ignore.py rename to cloudfoundry_client/operations/push/cf_ignore.py diff --git a/main/cloudfoundry_client/operations/push/file_helper.py b/cloudfoundry_client/operations/push/file_helper.py similarity index 100% rename from main/cloudfoundry_client/operations/push/file_helper.py rename to cloudfoundry_client/operations/push/file_helper.py diff --git a/main/cloudfoundry_client/operations/push/push.py b/cloudfoundry_client/operations/push/push.py similarity index 100% rename from main/cloudfoundry_client/operations/push/push.py rename to cloudfoundry_client/operations/push/push.py diff --git a/main/cloudfoundry_client/operations/push/validation/__init__.py b/cloudfoundry_client/operations/push/validation/__init__.py similarity index 100% rename from main/cloudfoundry_client/operations/push/validation/__init__.py rename to cloudfoundry_client/operations/push/validation/__init__.py diff --git a/main/cloudfoundry_client/operations/push/validation/manifest.py b/cloudfoundry_client/operations/push/validation/manifest.py similarity index 100% rename from main/cloudfoundry_client/operations/push/validation/manifest.py rename to cloudfoundry_client/operations/push/validation/manifest.py diff --git a/main/cloudfoundry_client/rlpgateway/__init__.py b/cloudfoundry_client/rlpgateway/__init__.py similarity index 100% rename from main/cloudfoundry_client/rlpgateway/__init__.py rename to cloudfoundry_client/rlpgateway/__init__.py diff --git a/main/cloudfoundry_client/rlpgateway/client.py b/cloudfoundry_client/rlpgateway/client.py similarity index 100% rename from main/cloudfoundry_client/rlpgateway/client.py rename to cloudfoundry_client/rlpgateway/client.py diff --git a/main/cloudfoundry_client/v2/__init__.py b/cloudfoundry_client/v2/__init__.py similarity index 100% rename from main/cloudfoundry_client/v2/__init__.py rename to cloudfoundry_client/v2/__init__.py diff --git a/main/cloudfoundry_client/v2/apps.py b/cloudfoundry_client/v2/apps.py similarity index 100% rename from main/cloudfoundry_client/v2/apps.py rename to cloudfoundry_client/v2/apps.py diff --git a/main/cloudfoundry_client/v2/buildpacks.py b/cloudfoundry_client/v2/buildpacks.py similarity index 100% rename from main/cloudfoundry_client/v2/buildpacks.py rename to cloudfoundry_client/v2/buildpacks.py diff --git a/main/cloudfoundry_client/v2/entities.py b/cloudfoundry_client/v2/entities.py similarity index 100% rename from main/cloudfoundry_client/v2/entities.py rename to cloudfoundry_client/v2/entities.py diff --git a/main/cloudfoundry_client/v2/events.py b/cloudfoundry_client/v2/events.py similarity index 100% rename from main/cloudfoundry_client/v2/events.py rename to cloudfoundry_client/v2/events.py diff --git a/main/cloudfoundry_client/v2/jobs.py b/cloudfoundry_client/v2/jobs.py similarity index 100% rename from main/cloudfoundry_client/v2/jobs.py rename to cloudfoundry_client/v2/jobs.py diff --git a/main/cloudfoundry_client/v2/resources.py b/cloudfoundry_client/v2/resources.py similarity index 100% rename from main/cloudfoundry_client/v2/resources.py rename to cloudfoundry_client/v2/resources.py diff --git a/main/cloudfoundry_client/v2/routes.py b/cloudfoundry_client/v2/routes.py similarity index 100% rename from main/cloudfoundry_client/v2/routes.py rename to cloudfoundry_client/v2/routes.py diff --git a/main/cloudfoundry_client/v2/service_bindings.py b/cloudfoundry_client/v2/service_bindings.py similarity index 100% rename from main/cloudfoundry_client/v2/service_bindings.py rename to cloudfoundry_client/v2/service_bindings.py diff --git a/main/cloudfoundry_client/v2/service_brokers.py b/cloudfoundry_client/v2/service_brokers.py similarity index 100% rename from main/cloudfoundry_client/v2/service_brokers.py rename to cloudfoundry_client/v2/service_brokers.py diff --git a/main/cloudfoundry_client/v2/service_instances.py b/cloudfoundry_client/v2/service_instances.py similarity index 100% rename from main/cloudfoundry_client/v2/service_instances.py rename to cloudfoundry_client/v2/service_instances.py diff --git a/main/cloudfoundry_client/v2/service_keys.py b/cloudfoundry_client/v2/service_keys.py similarity index 100% rename from main/cloudfoundry_client/v2/service_keys.py rename to cloudfoundry_client/v2/service_keys.py diff --git a/main/cloudfoundry_client/v2/service_plan_visibilities.py b/cloudfoundry_client/v2/service_plan_visibilities.py similarity index 100% rename from main/cloudfoundry_client/v2/service_plan_visibilities.py rename to cloudfoundry_client/v2/service_plan_visibilities.py diff --git a/main/cloudfoundry_client/v2/service_plans.py b/cloudfoundry_client/v2/service_plans.py similarity index 100% rename from main/cloudfoundry_client/v2/service_plans.py rename to cloudfoundry_client/v2/service_plans.py diff --git a/main/cloudfoundry_client/v2/spaces.py b/cloudfoundry_client/v2/spaces.py similarity index 100% rename from main/cloudfoundry_client/v2/spaces.py rename to cloudfoundry_client/v2/spaces.py diff --git a/main/cloudfoundry_client/v3/__init__.py b/cloudfoundry_client/v3/__init__.py similarity index 100% rename from main/cloudfoundry_client/v3/__init__.py rename to cloudfoundry_client/v3/__init__.py diff --git a/main/cloudfoundry_client/v3/apps.py b/cloudfoundry_client/v3/apps.py similarity index 100% rename from main/cloudfoundry_client/v3/apps.py rename to cloudfoundry_client/v3/apps.py diff --git a/main/cloudfoundry_client/v3/buildpacks.py b/cloudfoundry_client/v3/buildpacks.py similarity index 100% rename from main/cloudfoundry_client/v3/buildpacks.py rename to cloudfoundry_client/v3/buildpacks.py diff --git a/main/cloudfoundry_client/v3/domains.py b/cloudfoundry_client/v3/domains.py similarity index 100% rename from main/cloudfoundry_client/v3/domains.py rename to cloudfoundry_client/v3/domains.py diff --git a/main/cloudfoundry_client/v3/entities.py b/cloudfoundry_client/v3/entities.py similarity index 100% rename from main/cloudfoundry_client/v3/entities.py rename to cloudfoundry_client/v3/entities.py diff --git a/main/cloudfoundry_client/v3/feature_flags.py b/cloudfoundry_client/v3/feature_flags.py similarity index 100% rename from main/cloudfoundry_client/v3/feature_flags.py rename to cloudfoundry_client/v3/feature_flags.py diff --git a/main/cloudfoundry_client/v3/isolation_segments.py b/cloudfoundry_client/v3/isolation_segments.py similarity index 100% rename from main/cloudfoundry_client/v3/isolation_segments.py rename to cloudfoundry_client/v3/isolation_segments.py diff --git a/main/cloudfoundry_client/v3/jobs.py b/cloudfoundry_client/v3/jobs.py similarity index 100% rename from main/cloudfoundry_client/v3/jobs.py rename to cloudfoundry_client/v3/jobs.py diff --git a/main/cloudfoundry_client/v3/organization_quotas.py b/cloudfoundry_client/v3/organization_quotas.py similarity index 100% rename from main/cloudfoundry_client/v3/organization_quotas.py rename to cloudfoundry_client/v3/organization_quotas.py diff --git a/main/cloudfoundry_client/v3/organizations.py b/cloudfoundry_client/v3/organizations.py similarity index 100% rename from main/cloudfoundry_client/v3/organizations.py rename to cloudfoundry_client/v3/organizations.py diff --git a/main/cloudfoundry_client/v3/processes.py b/cloudfoundry_client/v3/processes.py similarity index 100% rename from main/cloudfoundry_client/v3/processes.py rename to cloudfoundry_client/v3/processes.py diff --git a/main/cloudfoundry_client/v3/roles.py b/cloudfoundry_client/v3/roles.py similarity index 100% rename from main/cloudfoundry_client/v3/roles.py rename to cloudfoundry_client/v3/roles.py diff --git a/main/cloudfoundry_client/v3/security_groups.py b/cloudfoundry_client/v3/security_groups.py similarity index 100% rename from main/cloudfoundry_client/v3/security_groups.py rename to cloudfoundry_client/v3/security_groups.py diff --git a/main/cloudfoundry_client/v3/service_brokers.py b/cloudfoundry_client/v3/service_brokers.py similarity index 100% rename from main/cloudfoundry_client/v3/service_brokers.py rename to cloudfoundry_client/v3/service_brokers.py diff --git a/main/cloudfoundry_client/v3/service_credential_bindings.py b/cloudfoundry_client/v3/service_credential_bindings.py similarity index 100% rename from main/cloudfoundry_client/v3/service_credential_bindings.py rename to cloudfoundry_client/v3/service_credential_bindings.py diff --git a/main/cloudfoundry_client/v3/service_instances.py b/cloudfoundry_client/v3/service_instances.py similarity index 100% rename from main/cloudfoundry_client/v3/service_instances.py rename to cloudfoundry_client/v3/service_instances.py diff --git a/main/cloudfoundry_client/v3/service_offerings.py b/cloudfoundry_client/v3/service_offerings.py similarity index 100% rename from main/cloudfoundry_client/v3/service_offerings.py rename to cloudfoundry_client/v3/service_offerings.py diff --git a/main/cloudfoundry_client/v3/service_plans.py b/cloudfoundry_client/v3/service_plans.py similarity index 100% rename from main/cloudfoundry_client/v3/service_plans.py rename to cloudfoundry_client/v3/service_plans.py diff --git a/main/cloudfoundry_client/v3/spaces.py b/cloudfoundry_client/v3/spaces.py similarity index 100% rename from main/cloudfoundry_client/v3/spaces.py rename to cloudfoundry_client/v3/spaces.py diff --git a/main/cloudfoundry_client/v3/tasks.py b/cloudfoundry_client/v3/tasks.py similarity index 100% rename from main/cloudfoundry_client/v3/tasks.py rename to cloudfoundry_client/v3/tasks.py From fa2b9df4efaca1c06b96111a2dd4f1902741039f Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Fri, 12 Jul 2024 13:25:36 +0200 Subject: [PATCH 181/264] fixes(convention) rename test to tests --- {test => tests}/__init__.py | 0 {test => tests}/abstract_test_case.py | 0 {test => tests}/fake_requests.py | 0 .../fake/GET_invalid_entity_with_invalid_entity_type.json | 0 .../fixtures/fake/GET_invalid_entity_with_null_entity.json | 0 .../fixtures/fake/GET_invalid_entity_without_entity.json | 0 {test => tests}/fixtures/fake/GET_multi_page_0_response.json | 0 {test => tests}/fixtures/fake/GET_multi_page_1_response.json | 0 {test => tests}/fixtures/fake/GET_response.json | 0 {test => tests}/fixtures/fake/GET_{id}_response.json | 0 {test => tests}/fixtures/fake/manifest_main.yml | 0 .../fixtures/networking/v1/external/policies/GET_response.json | 0 {test => tests}/fixtures/operations/manifest.yml | 0 {test => tests}/fixtures/operations/manifest_complex.yml | 0 {test => tests}/fixtures/operations/manifest_empty.yml | 0 {test => tests}/fixtures/recents/GET_response.bin | 0 {test => tests}/fixtures/v2/apps/GET_response.json | 0 .../fixtures/v2/apps/GET_space_guid_name_response.json | 0 {test => tests}/fixtures/v2/apps/GET_{id}_env_response.json | 0 {test => tests}/fixtures/v2/apps/GET_{id}_instances_response.json | 0 .../fixtures/v2/apps/GET_{id}_instances_stopped_response.json | 0 {test => tests}/fixtures/v2/apps/GET_{id}_response.json | 0 {test => tests}/fixtures/v2/apps/GET_{id}_routes_response.json | 0 .../fixtures/v2/apps/GET_{id}_service_bindings_response.json | 0 {test => tests}/fixtures/v2/apps/GET_{id}_stats_response.json | 0 {test => tests}/fixtures/v2/apps/GET_{id}_summary_response.json | 0 {test => tests}/fixtures/v2/apps/POST_response.json | 0 {test => tests}/fixtures/v2/apps/POST_{id}_restage_response.json | 0 {test => tests}/fixtures/v2/apps/PUT_{id}_response.json | 0 .../fixtures/v2/apps/PUT_{id}_routes_{route_id}_response.json | 0 {test => tests}/fixtures/v2/buildpacks/GET_response.json | 0 {test => tests}/fixtures/v2/buildpacks/GET_{id}_response.json | 0 {test => tests}/fixtures/v2/buildpacks/PUT_{id}_response.json | 0 .../v2/events/GET_response_audit.route.delete-request.json | 0 {test => tests}/fixtures/v2/organizations/GET_response.json | 0 {test => tests}/fixtures/v2/organizations/GET_{id}_response.json | 0 {test => tests}/fixtures/v2/routes/GET_response.json | 0 {test => tests}/fixtures/v2/routes/GET_{id}_response.json | 0 {test => tests}/fixtures/v2/service_bindings/GET_response.json | 0 .../fixtures/v2/service_bindings/GET_{id}_response.json | 0 {test => tests}/fixtures/v2/service_bindings/POST_response.json | 0 {test => tests}/fixtures/v2/service_brokers/GET_response.json | 0 .../fixtures/v2/service_brokers/GET_{id}_response.json | 0 {test => tests}/fixtures/v2/service_brokers/POST_response.json | 0 .../fixtures/v2/service_brokers/PUT_{id}_response.json | 0 {test => tests}/fixtures/v2/service_instances/GET_response.json | 0 .../fixtures/v2/service_instances/GET_{id}_response.json | 0 {test => tests}/fixtures/v2/service_instances/POST_response.json | 0 .../fixtures/v2/service_instances/PUT_{id}_response.json | 0 {test => tests}/fixtures/v2/service_keys/GET_response.json | 0 {test => tests}/fixtures/v2/service_keys/GET_{id}_response.json | 0 {test => tests}/fixtures/v2/service_keys/POST_response.json | 0 .../fixtures/v2/service_plan_visibilities/GET_response.json | 0 .../fixtures/v2/service_plan_visibilities/GET_{id}_response.json | 0 .../fixtures/v2/service_plan_visibilities/POST_response.json | 0 .../fixtures/v2/service_plan_visibilities/PUT_{id}_response.json | 0 {test => tests}/fixtures/v2/service_plans/GET_response.json | 0 {test => tests}/fixtures/v2/service_plans/GET_{id}_response.json | 0 {test => tests}/fixtures/v2/services/GET_response.json | 0 {test => tests}/fixtures/v2/services/GET_{id}_response.json | 0 {test => tests}/fixtures/v2/spaces/GET_response.json | 0 {test => tests}/fixtures/v2/spaces/GET_{id}_response.json | 0 {test => tests}/fixtures/v3/apps/GET_response.json | 0 {test => tests}/fixtures/v3/apps/GET_response_include_space.json | 0 {test => tests}/fixtures/v3/apps/GET_{id}_env_response.json | 0 .../fixtures/v3/apps/GET_{id}_environment_variables_response.json | 0 {test => tests}/fixtures/v3/apps/GET_{id}_response.json | 0 .../fixtures/v3/apps/GET_{id}_response_include_space_and_org.json | 0 {test => tests}/fixtures/v3/apps/GET_{id}_routes_response.json | 0 .../fixtures/v3/apps/POST_{id}_actions_start_response.json | 0 {test => tests}/fixtures/v3/buildpacks/GET_response.json | 0 {test => tests}/fixtures/v3/buildpacks/GET_{id}_response.json | 0 {test => tests}/fixtures/v3/buildpacks/PATCH_{id}_response.json | 0 {test => tests}/fixtures/v3/buildpacks/POST_response.json | 0 {test => tests}/fixtures/v3/domains/GET_response.json | 0 {test => tests}/fixtures/v3/domains/GET_{id}_response.json | 0 {test => tests}/fixtures/v3/domains/PATCH_{id}_response.json | 0 {test => tests}/fixtures/v3/domains/POST_response.json | 0 .../POST_{id}_relationships_shared_organizations_response.json | 0 {test => tests}/fixtures/v3/feature_flags/GET_response.json | 0 {test => tests}/fixtures/v3/feature_flags/GET_{id}_response.json | 0 .../fixtures/v3/feature_flags/PATCH_{id}_response.json | 0 {test => tests}/fixtures/v3/isolation_segments/GET_response.json | 0 .../GET_{id}_relationships_organizations_response.json | 0 .../GET_{id}_relationships_spaces_response.json | 0 .../fixtures/v3/isolation_segments/GET_{id}_response.json | 0 .../fixtures/v3/isolation_segments/PATCH_{id}_response.json | 0 {test => tests}/fixtures/v3/isolation_segments/POST_response.json | 0 .../POST_{id}_relationships_organizations_response.json | 0 {test => tests}/fixtures/v3/jobs/GET_{id}_complete_response.json | 0 {test => tests}/fixtures/v3/jobs/GET_{id}_failed_response.json | 0 .../fixtures/v3/jobs/GET_{id}_processing_response.json | 0 {test => tests}/fixtures/v3/organization_quotas/GET_response.json | 0 .../fixtures/v3/organization_quotas/GET_{id}_response.json | 0 .../fixtures/v3/organization_quotas/PATCH_{id}_response.json | 0 .../fixtures/v3/organization_quotas/POST_response.json | 0 .../v3/organization_quotas/POST_{id}_organizations_response.json | 0 {test => tests}/fixtures/v3/organizations/GET_response.json | 0 .../v3/organizations/GET_{id}_default_domain_response.json | 0 ...GET_{id}_relationships_default_isolation_segment_response.json | 0 {test => tests}/fixtures/v3/organizations/GET_{id}_response.json | 0 .../v3/organizations/GET_{id}_usage_summary_response.json | 0 ...TCH_{id}_relationships_default_isolation_segment_response.json | 0 .../fixtures/v3/organizations/PATCH_{id}_response.json | 0 {test => tests}/fixtures/v3/organizations/POST_response.json | 0 {test => tests}/fixtures/v3/processes/GET_response.json | 0 {test => tests}/fixtures/v3/processes/GET_{id}_response.json | 0 .../fixtures/v3/processes/GET_{id}_stats_response.json | 0 {test => tests}/fixtures/v3/roles/GET_response.json | 0 {test => tests}/fixtures/v3/roles/GET_{id}_response.json | 0 {test => tests}/fixtures/v3/security_groups/GET_response.json | 0 .../fixtures/v3/security_groups/GET_{id}_response.json | 0 .../fixtures/v3/security_groups/PATCH_{id}_response.json | 0 {test => tests}/fixtures/v3/security_groups/POST_response.json | 0 .../POST_{id}_relationships_running_spaces_response.json | 0 .../POST_{id}_relationships_staging_spaces_response.json | 0 {test => tests}/fixtures/v3/service_brokers/GET_response.json | 0 .../fixtures/v3/service_brokers/GET_{id}_response.json | 0 .../fixtures/v3/service_brokers/PATCH_{id}_response.json | 0 {test => tests}/fixtures/v3/service_brokers/POST_response.json | 0 .../fixtures/v3/service_credential_bindings/GET_response.json | 0 .../v3/service_credential_bindings/GET_{id}_details_response.json | 0 .../service_credential_bindings/GET_{id}_parameters_response.json | 0 .../v3/service_credential_bindings/GET_{id}_response.json | 0 .../fixtures/v3/service_credential_bindings/POST_response.json | 0 {test => tests}/fixtures/v3/service_instances/GET_response.json | 0 .../v3/service_instances/GET_{id}_credentials_response.json | 0 .../v3/service_instances/GET_{id}_permissions_response.json | 0 .../fixtures/v3/service_instances/GET_{id}_response.json | 0 {test => tests}/fixtures/v3/service_offerings/GET_response.json | 0 .../fixtures/v3/service_offerings/GET_{id}_response.json | 0 .../fixtures/v3/service_offerings/PATCH_{id}_response.json | 0 {test => tests}/fixtures/v3/service_plans/GET_response.json | 0 {test => tests}/fixtures/v3/service_plans/GET_{id}_response.json | 0 .../fixtures/v3/service_plans/GET_{id}_visibility_response.json | 0 .../fixtures/v3/service_plans/PATCH_{id}_response.json | 0 .../PATCH_{id}_visibility_organizations_response.json | 0 .../v3/service_plans/PATCH_{id}_visibility_type_response.json | 0 .../fixtures/v3/service_plans/POST_{id}_visibility_response.json | 0 {test => tests}/fixtures/v3/spaces/GET_response.json | 0 {test => tests}/fixtures/v3/spaces/GET_response_include_org.json | 0 .../spaces/GET_{id}_relationships_isolation_segment_response.json | 0 {test => tests}/fixtures/v3/spaces/GET_{id}_response.json | 0 .../fixtures/v3/spaces/GET_{id}_response_include_org.json | 0 {test => tests}/fixtures/v3/spaces/POST_response.json | 0 .../POST_{id}_relationships_isolation_segment_response.json | 0 {test => tests}/fixtures/v3/tasks/GET_response.json | 0 {test => tests}/fixtures/v3/tasks/GET_{id}_response.json | 0 {test => tests}/fixtures/v3/tasks/POST_response.json | 0 .../fixtures/v3/tasks/POST_{id}_actions_cancel_response.json | 0 {test => tests}/networking/__init__.py | 0 {test => tests}/networking/v1/__init__.py | 0 {test => tests}/networking/v1/external/__init__.py | 0 {test => tests}/networking/v1/external/test_policies.py | 0 {test => tests}/operations/__init__.py | 0 {test => tests}/operations/push/__init__.py | 0 {test => tests}/operations/push/test_cf_ignore.py | 0 {test => tests}/operations/push/test_file_helper.py | 0 {test => tests}/operations/push/test_push.py | 0 {test => tests}/operations/push/validation/__init__.py | 0 .../operations/push/validation/test_manifest_reader.py | 0 {test => tests}/test_client.py | 0 {test => tests}/test_request_object.py | 0 {test => tests}/v2/__init__.py | 0 {test => tests}/v2/test_apps.py | 0 {test => tests}/v2/test_buildpacks.py | 0 {test => tests}/v2/test_doppler.py | 0 {test => tests}/v2/test_entities.py | 0 {test => tests}/v2/test_events.py | 0 {test => tests}/v2/test_organizations.py | 0 {test => tests}/v2/test_routes.py | 0 {test => tests}/v2/test_service_bindings.py | 0 {test => tests}/v2/test_service_brokers.py | 0 {test => tests}/v2/test_service_instances.py | 0 {test => tests}/v2/test_service_keys.py | 0 {test => tests}/v2/test_service_plan_visibilities.py | 0 {test => tests}/v2/test_service_plans.py | 0 {test => tests}/v2/test_services.py | 0 {test => tests}/v2/test_spaces.py | 0 {test => tests}/v3/__init__.py | 0 {test => tests}/v3/test_apps.py | 0 {test => tests}/v3/test_buildpacks.py | 0 {test => tests}/v3/test_domains.py | 0 {test => tests}/v3/test_entities.py | 0 {test => tests}/v3/test_feature_flags.py | 0 {test => tests}/v3/test_isolation_segments.py | 0 {test => tests}/v3/test_jobs.py | 0 {test => tests}/v3/test_organization_quotas.py | 0 {test => tests}/v3/test_organizations.py | 0 {test => tests}/v3/test_processes.py | 0 {test => tests}/v3/test_roles.py | 0 {test => tests}/v3/test_security_groups.py | 0 {test => tests}/v3/test_service_brokers.py | 0 {test => tests}/v3/test_service_credential_bindings.py | 0 {test => tests}/v3/test_service_instances.py | 0 {test => tests}/v3/test_service_offerings.py | 0 {test => tests}/v3/test_service_plans.py | 0 {test => tests}/v3/test_spaces.py | 0 {test => tests}/v3/test_tasks.py | 0 199 files changed, 0 insertions(+), 0 deletions(-) rename {test => tests}/__init__.py (100%) rename {test => tests}/abstract_test_case.py (100%) rename {test => tests}/fake_requests.py (100%) rename {test => tests}/fixtures/fake/GET_invalid_entity_with_invalid_entity_type.json (100%) rename {test => tests}/fixtures/fake/GET_invalid_entity_with_null_entity.json (100%) rename {test => tests}/fixtures/fake/GET_invalid_entity_without_entity.json (100%) rename {test => tests}/fixtures/fake/GET_multi_page_0_response.json (100%) rename {test => tests}/fixtures/fake/GET_multi_page_1_response.json (100%) rename {test => tests}/fixtures/fake/GET_response.json (100%) rename {test => tests}/fixtures/fake/GET_{id}_response.json (100%) rename {test => tests}/fixtures/fake/manifest_main.yml (100%) rename {test => tests}/fixtures/networking/v1/external/policies/GET_response.json (100%) rename {test => tests}/fixtures/operations/manifest.yml (100%) rename {test => tests}/fixtures/operations/manifest_complex.yml (100%) rename {test => tests}/fixtures/operations/manifest_empty.yml (100%) rename {test => tests}/fixtures/recents/GET_response.bin (100%) rename {test => tests}/fixtures/v2/apps/GET_response.json (100%) rename {test => tests}/fixtures/v2/apps/GET_space_guid_name_response.json (100%) rename {test => tests}/fixtures/v2/apps/GET_{id}_env_response.json (100%) rename {test => tests}/fixtures/v2/apps/GET_{id}_instances_response.json (100%) rename {test => tests}/fixtures/v2/apps/GET_{id}_instances_stopped_response.json (100%) rename {test => tests}/fixtures/v2/apps/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v2/apps/GET_{id}_routes_response.json (100%) rename {test => tests}/fixtures/v2/apps/GET_{id}_service_bindings_response.json (100%) rename {test => tests}/fixtures/v2/apps/GET_{id}_stats_response.json (100%) rename {test => tests}/fixtures/v2/apps/GET_{id}_summary_response.json (100%) rename {test => tests}/fixtures/v2/apps/POST_response.json (100%) rename {test => tests}/fixtures/v2/apps/POST_{id}_restage_response.json (100%) rename {test => tests}/fixtures/v2/apps/PUT_{id}_response.json (100%) rename {test => tests}/fixtures/v2/apps/PUT_{id}_routes_{route_id}_response.json (100%) rename {test => tests}/fixtures/v2/buildpacks/GET_response.json (100%) rename {test => tests}/fixtures/v2/buildpacks/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v2/buildpacks/PUT_{id}_response.json (100%) rename {test => tests}/fixtures/v2/events/GET_response_audit.route.delete-request.json (100%) rename {test => tests}/fixtures/v2/organizations/GET_response.json (100%) rename {test => tests}/fixtures/v2/organizations/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v2/routes/GET_response.json (100%) rename {test => tests}/fixtures/v2/routes/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v2/service_bindings/GET_response.json (100%) rename {test => tests}/fixtures/v2/service_bindings/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v2/service_bindings/POST_response.json (100%) rename {test => tests}/fixtures/v2/service_brokers/GET_response.json (100%) rename {test => tests}/fixtures/v2/service_brokers/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v2/service_brokers/POST_response.json (100%) rename {test => tests}/fixtures/v2/service_brokers/PUT_{id}_response.json (100%) rename {test => tests}/fixtures/v2/service_instances/GET_response.json (100%) rename {test => tests}/fixtures/v2/service_instances/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v2/service_instances/POST_response.json (100%) rename {test => tests}/fixtures/v2/service_instances/PUT_{id}_response.json (100%) rename {test => tests}/fixtures/v2/service_keys/GET_response.json (100%) rename {test => tests}/fixtures/v2/service_keys/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v2/service_keys/POST_response.json (100%) rename {test => tests}/fixtures/v2/service_plan_visibilities/GET_response.json (100%) rename {test => tests}/fixtures/v2/service_plan_visibilities/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v2/service_plan_visibilities/POST_response.json (100%) rename {test => tests}/fixtures/v2/service_plan_visibilities/PUT_{id}_response.json (100%) rename {test => tests}/fixtures/v2/service_plans/GET_response.json (100%) rename {test => tests}/fixtures/v2/service_plans/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v2/services/GET_response.json (100%) rename {test => tests}/fixtures/v2/services/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v2/spaces/GET_response.json (100%) rename {test => tests}/fixtures/v2/spaces/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v3/apps/GET_response.json (100%) rename {test => tests}/fixtures/v3/apps/GET_response_include_space.json (100%) rename {test => tests}/fixtures/v3/apps/GET_{id}_env_response.json (100%) rename {test => tests}/fixtures/v3/apps/GET_{id}_environment_variables_response.json (100%) rename {test => tests}/fixtures/v3/apps/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v3/apps/GET_{id}_response_include_space_and_org.json (100%) rename {test => tests}/fixtures/v3/apps/GET_{id}_routes_response.json (100%) rename {test => tests}/fixtures/v3/apps/POST_{id}_actions_start_response.json (100%) rename {test => tests}/fixtures/v3/buildpacks/GET_response.json (100%) rename {test => tests}/fixtures/v3/buildpacks/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v3/buildpacks/PATCH_{id}_response.json (100%) rename {test => tests}/fixtures/v3/buildpacks/POST_response.json (100%) rename {test => tests}/fixtures/v3/domains/GET_response.json (100%) rename {test => tests}/fixtures/v3/domains/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v3/domains/PATCH_{id}_response.json (100%) rename {test => tests}/fixtures/v3/domains/POST_response.json (100%) rename {test => tests}/fixtures/v3/domains/POST_{id}_relationships_shared_organizations_response.json (100%) rename {test => tests}/fixtures/v3/feature_flags/GET_response.json (100%) rename {test => tests}/fixtures/v3/feature_flags/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v3/feature_flags/PATCH_{id}_response.json (100%) rename {test => tests}/fixtures/v3/isolation_segments/GET_response.json (100%) rename {test => tests}/fixtures/v3/isolation_segments/GET_{id}_relationships_organizations_response.json (100%) rename {test => tests}/fixtures/v3/isolation_segments/GET_{id}_relationships_spaces_response.json (100%) rename {test => tests}/fixtures/v3/isolation_segments/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v3/isolation_segments/PATCH_{id}_response.json (100%) rename {test => tests}/fixtures/v3/isolation_segments/POST_response.json (100%) rename {test => tests}/fixtures/v3/isolation_segments/POST_{id}_relationships_organizations_response.json (100%) rename {test => tests}/fixtures/v3/jobs/GET_{id}_complete_response.json (100%) rename {test => tests}/fixtures/v3/jobs/GET_{id}_failed_response.json (100%) rename {test => tests}/fixtures/v3/jobs/GET_{id}_processing_response.json (100%) rename {test => tests}/fixtures/v3/organization_quotas/GET_response.json (100%) rename {test => tests}/fixtures/v3/organization_quotas/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v3/organization_quotas/PATCH_{id}_response.json (100%) rename {test => tests}/fixtures/v3/organization_quotas/POST_response.json (100%) rename {test => tests}/fixtures/v3/organization_quotas/POST_{id}_organizations_response.json (100%) rename {test => tests}/fixtures/v3/organizations/GET_response.json (100%) rename {test => tests}/fixtures/v3/organizations/GET_{id}_default_domain_response.json (100%) rename {test => tests}/fixtures/v3/organizations/GET_{id}_relationships_default_isolation_segment_response.json (100%) rename {test => tests}/fixtures/v3/organizations/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v3/organizations/GET_{id}_usage_summary_response.json (100%) rename {test => tests}/fixtures/v3/organizations/PATCH_{id}_relationships_default_isolation_segment_response.json (100%) rename {test => tests}/fixtures/v3/organizations/PATCH_{id}_response.json (100%) rename {test => tests}/fixtures/v3/organizations/POST_response.json (100%) rename {test => tests}/fixtures/v3/processes/GET_response.json (100%) rename {test => tests}/fixtures/v3/processes/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v3/processes/GET_{id}_stats_response.json (100%) rename {test => tests}/fixtures/v3/roles/GET_response.json (100%) rename {test => tests}/fixtures/v3/roles/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v3/security_groups/GET_response.json (100%) rename {test => tests}/fixtures/v3/security_groups/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v3/security_groups/PATCH_{id}_response.json (100%) rename {test => tests}/fixtures/v3/security_groups/POST_response.json (100%) rename {test => tests}/fixtures/v3/security_groups/POST_{id}_relationships_running_spaces_response.json (100%) rename {test => tests}/fixtures/v3/security_groups/POST_{id}_relationships_staging_spaces_response.json (100%) rename {test => tests}/fixtures/v3/service_brokers/GET_response.json (100%) rename {test => tests}/fixtures/v3/service_brokers/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v3/service_brokers/PATCH_{id}_response.json (100%) rename {test => tests}/fixtures/v3/service_brokers/POST_response.json (100%) rename {test => tests}/fixtures/v3/service_credential_bindings/GET_response.json (100%) rename {test => tests}/fixtures/v3/service_credential_bindings/GET_{id}_details_response.json (100%) rename {test => tests}/fixtures/v3/service_credential_bindings/GET_{id}_parameters_response.json (100%) rename {test => tests}/fixtures/v3/service_credential_bindings/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v3/service_credential_bindings/POST_response.json (100%) rename {test => tests}/fixtures/v3/service_instances/GET_response.json (100%) rename {test => tests}/fixtures/v3/service_instances/GET_{id}_credentials_response.json (100%) rename {test => tests}/fixtures/v3/service_instances/GET_{id}_permissions_response.json (100%) rename {test => tests}/fixtures/v3/service_instances/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v3/service_offerings/GET_response.json (100%) rename {test => tests}/fixtures/v3/service_offerings/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v3/service_offerings/PATCH_{id}_response.json (100%) rename {test => tests}/fixtures/v3/service_plans/GET_response.json (100%) rename {test => tests}/fixtures/v3/service_plans/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v3/service_plans/GET_{id}_visibility_response.json (100%) rename {test => tests}/fixtures/v3/service_plans/PATCH_{id}_response.json (100%) rename {test => tests}/fixtures/v3/service_plans/PATCH_{id}_visibility_organizations_response.json (100%) rename {test => tests}/fixtures/v3/service_plans/PATCH_{id}_visibility_type_response.json (100%) rename {test => tests}/fixtures/v3/service_plans/POST_{id}_visibility_response.json (100%) rename {test => tests}/fixtures/v3/spaces/GET_response.json (100%) rename {test => tests}/fixtures/v3/spaces/GET_response_include_org.json (100%) rename {test => tests}/fixtures/v3/spaces/GET_{id}_relationships_isolation_segment_response.json (100%) rename {test => tests}/fixtures/v3/spaces/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v3/spaces/GET_{id}_response_include_org.json (100%) rename {test => tests}/fixtures/v3/spaces/POST_response.json (100%) rename {test => tests}/fixtures/v3/spaces/POST_{id}_relationships_isolation_segment_response.json (100%) rename {test => tests}/fixtures/v3/tasks/GET_response.json (100%) rename {test => tests}/fixtures/v3/tasks/GET_{id}_response.json (100%) rename {test => tests}/fixtures/v3/tasks/POST_response.json (100%) rename {test => tests}/fixtures/v3/tasks/POST_{id}_actions_cancel_response.json (100%) rename {test => tests}/networking/__init__.py (100%) rename {test => tests}/networking/v1/__init__.py (100%) rename {test => tests}/networking/v1/external/__init__.py (100%) rename {test => tests}/networking/v1/external/test_policies.py (100%) rename {test => tests}/operations/__init__.py (100%) rename {test => tests}/operations/push/__init__.py (100%) rename {test => tests}/operations/push/test_cf_ignore.py (100%) rename {test => tests}/operations/push/test_file_helper.py (100%) rename {test => tests}/operations/push/test_push.py (100%) rename {test => tests}/operations/push/validation/__init__.py (100%) rename {test => tests}/operations/push/validation/test_manifest_reader.py (100%) rename {test => tests}/test_client.py (100%) rename {test => tests}/test_request_object.py (100%) rename {test => tests}/v2/__init__.py (100%) rename {test => tests}/v2/test_apps.py (100%) rename {test => tests}/v2/test_buildpacks.py (100%) rename {test => tests}/v2/test_doppler.py (100%) rename {test => tests}/v2/test_entities.py (100%) rename {test => tests}/v2/test_events.py (100%) rename {test => tests}/v2/test_organizations.py (100%) rename {test => tests}/v2/test_routes.py (100%) rename {test => tests}/v2/test_service_bindings.py (100%) rename {test => tests}/v2/test_service_brokers.py (100%) rename {test => tests}/v2/test_service_instances.py (100%) rename {test => tests}/v2/test_service_keys.py (100%) rename {test => tests}/v2/test_service_plan_visibilities.py (100%) rename {test => tests}/v2/test_service_plans.py (100%) rename {test => tests}/v2/test_services.py (100%) rename {test => tests}/v2/test_spaces.py (100%) rename {test => tests}/v3/__init__.py (100%) rename {test => tests}/v3/test_apps.py (100%) rename {test => tests}/v3/test_buildpacks.py (100%) rename {test => tests}/v3/test_domains.py (100%) rename {test => tests}/v3/test_entities.py (100%) rename {test => tests}/v3/test_feature_flags.py (100%) rename {test => tests}/v3/test_isolation_segments.py (100%) rename {test => tests}/v3/test_jobs.py (100%) rename {test => tests}/v3/test_organization_quotas.py (100%) rename {test => tests}/v3/test_organizations.py (100%) rename {test => tests}/v3/test_processes.py (100%) rename {test => tests}/v3/test_roles.py (100%) rename {test => tests}/v3/test_security_groups.py (100%) rename {test => tests}/v3/test_service_brokers.py (100%) rename {test => tests}/v3/test_service_credential_bindings.py (100%) rename {test => tests}/v3/test_service_instances.py (100%) rename {test => tests}/v3/test_service_offerings.py (100%) rename {test => tests}/v3/test_service_plans.py (100%) rename {test => tests}/v3/test_spaces.py (100%) rename {test => tests}/v3/test_tasks.py (100%) diff --git a/test/__init__.py b/tests/__init__.py similarity index 100% rename from test/__init__.py rename to tests/__init__.py diff --git a/test/abstract_test_case.py b/tests/abstract_test_case.py similarity index 100% rename from test/abstract_test_case.py rename to tests/abstract_test_case.py diff --git a/test/fake_requests.py b/tests/fake_requests.py similarity index 100% rename from test/fake_requests.py rename to tests/fake_requests.py diff --git a/test/fixtures/fake/GET_invalid_entity_with_invalid_entity_type.json b/tests/fixtures/fake/GET_invalid_entity_with_invalid_entity_type.json similarity index 100% rename from test/fixtures/fake/GET_invalid_entity_with_invalid_entity_type.json rename to tests/fixtures/fake/GET_invalid_entity_with_invalid_entity_type.json diff --git a/test/fixtures/fake/GET_invalid_entity_with_null_entity.json b/tests/fixtures/fake/GET_invalid_entity_with_null_entity.json similarity index 100% rename from test/fixtures/fake/GET_invalid_entity_with_null_entity.json rename to tests/fixtures/fake/GET_invalid_entity_with_null_entity.json diff --git a/test/fixtures/fake/GET_invalid_entity_without_entity.json b/tests/fixtures/fake/GET_invalid_entity_without_entity.json similarity index 100% rename from test/fixtures/fake/GET_invalid_entity_without_entity.json rename to tests/fixtures/fake/GET_invalid_entity_without_entity.json diff --git a/test/fixtures/fake/GET_multi_page_0_response.json b/tests/fixtures/fake/GET_multi_page_0_response.json similarity index 100% rename from test/fixtures/fake/GET_multi_page_0_response.json rename to tests/fixtures/fake/GET_multi_page_0_response.json diff --git a/test/fixtures/fake/GET_multi_page_1_response.json b/tests/fixtures/fake/GET_multi_page_1_response.json similarity index 100% rename from test/fixtures/fake/GET_multi_page_1_response.json rename to tests/fixtures/fake/GET_multi_page_1_response.json diff --git a/test/fixtures/fake/GET_response.json b/tests/fixtures/fake/GET_response.json similarity index 100% rename from test/fixtures/fake/GET_response.json rename to tests/fixtures/fake/GET_response.json diff --git a/test/fixtures/fake/GET_{id}_response.json b/tests/fixtures/fake/GET_{id}_response.json similarity index 100% rename from test/fixtures/fake/GET_{id}_response.json rename to tests/fixtures/fake/GET_{id}_response.json diff --git a/test/fixtures/fake/manifest_main.yml b/tests/fixtures/fake/manifest_main.yml similarity index 100% rename from test/fixtures/fake/manifest_main.yml rename to tests/fixtures/fake/manifest_main.yml diff --git a/test/fixtures/networking/v1/external/policies/GET_response.json b/tests/fixtures/networking/v1/external/policies/GET_response.json similarity index 100% rename from test/fixtures/networking/v1/external/policies/GET_response.json rename to tests/fixtures/networking/v1/external/policies/GET_response.json diff --git a/test/fixtures/operations/manifest.yml b/tests/fixtures/operations/manifest.yml similarity index 100% rename from test/fixtures/operations/manifest.yml rename to tests/fixtures/operations/manifest.yml diff --git a/test/fixtures/operations/manifest_complex.yml b/tests/fixtures/operations/manifest_complex.yml similarity index 100% rename from test/fixtures/operations/manifest_complex.yml rename to tests/fixtures/operations/manifest_complex.yml diff --git a/test/fixtures/operations/manifest_empty.yml b/tests/fixtures/operations/manifest_empty.yml similarity index 100% rename from test/fixtures/operations/manifest_empty.yml rename to tests/fixtures/operations/manifest_empty.yml diff --git a/test/fixtures/recents/GET_response.bin b/tests/fixtures/recents/GET_response.bin similarity index 100% rename from test/fixtures/recents/GET_response.bin rename to tests/fixtures/recents/GET_response.bin diff --git a/test/fixtures/v2/apps/GET_response.json b/tests/fixtures/v2/apps/GET_response.json similarity index 100% rename from test/fixtures/v2/apps/GET_response.json rename to tests/fixtures/v2/apps/GET_response.json diff --git a/test/fixtures/v2/apps/GET_space_guid_name_response.json b/tests/fixtures/v2/apps/GET_space_guid_name_response.json similarity index 100% rename from test/fixtures/v2/apps/GET_space_guid_name_response.json rename to tests/fixtures/v2/apps/GET_space_guid_name_response.json diff --git a/test/fixtures/v2/apps/GET_{id}_env_response.json b/tests/fixtures/v2/apps/GET_{id}_env_response.json similarity index 100% rename from test/fixtures/v2/apps/GET_{id}_env_response.json rename to tests/fixtures/v2/apps/GET_{id}_env_response.json diff --git a/test/fixtures/v2/apps/GET_{id}_instances_response.json b/tests/fixtures/v2/apps/GET_{id}_instances_response.json similarity index 100% rename from test/fixtures/v2/apps/GET_{id}_instances_response.json rename to tests/fixtures/v2/apps/GET_{id}_instances_response.json diff --git a/test/fixtures/v2/apps/GET_{id}_instances_stopped_response.json b/tests/fixtures/v2/apps/GET_{id}_instances_stopped_response.json similarity index 100% rename from test/fixtures/v2/apps/GET_{id}_instances_stopped_response.json rename to tests/fixtures/v2/apps/GET_{id}_instances_stopped_response.json diff --git a/test/fixtures/v2/apps/GET_{id}_response.json b/tests/fixtures/v2/apps/GET_{id}_response.json similarity index 100% rename from test/fixtures/v2/apps/GET_{id}_response.json rename to tests/fixtures/v2/apps/GET_{id}_response.json diff --git a/test/fixtures/v2/apps/GET_{id}_routes_response.json b/tests/fixtures/v2/apps/GET_{id}_routes_response.json similarity index 100% rename from test/fixtures/v2/apps/GET_{id}_routes_response.json rename to tests/fixtures/v2/apps/GET_{id}_routes_response.json diff --git a/test/fixtures/v2/apps/GET_{id}_service_bindings_response.json b/tests/fixtures/v2/apps/GET_{id}_service_bindings_response.json similarity index 100% rename from test/fixtures/v2/apps/GET_{id}_service_bindings_response.json rename to tests/fixtures/v2/apps/GET_{id}_service_bindings_response.json diff --git a/test/fixtures/v2/apps/GET_{id}_stats_response.json b/tests/fixtures/v2/apps/GET_{id}_stats_response.json similarity index 100% rename from test/fixtures/v2/apps/GET_{id}_stats_response.json rename to tests/fixtures/v2/apps/GET_{id}_stats_response.json diff --git a/test/fixtures/v2/apps/GET_{id}_summary_response.json b/tests/fixtures/v2/apps/GET_{id}_summary_response.json similarity index 100% rename from test/fixtures/v2/apps/GET_{id}_summary_response.json rename to tests/fixtures/v2/apps/GET_{id}_summary_response.json diff --git a/test/fixtures/v2/apps/POST_response.json b/tests/fixtures/v2/apps/POST_response.json similarity index 100% rename from test/fixtures/v2/apps/POST_response.json rename to tests/fixtures/v2/apps/POST_response.json diff --git a/test/fixtures/v2/apps/POST_{id}_restage_response.json b/tests/fixtures/v2/apps/POST_{id}_restage_response.json similarity index 100% rename from test/fixtures/v2/apps/POST_{id}_restage_response.json rename to tests/fixtures/v2/apps/POST_{id}_restage_response.json diff --git a/test/fixtures/v2/apps/PUT_{id}_response.json b/tests/fixtures/v2/apps/PUT_{id}_response.json similarity index 100% rename from test/fixtures/v2/apps/PUT_{id}_response.json rename to tests/fixtures/v2/apps/PUT_{id}_response.json diff --git a/test/fixtures/v2/apps/PUT_{id}_routes_{route_id}_response.json b/tests/fixtures/v2/apps/PUT_{id}_routes_{route_id}_response.json similarity index 100% rename from test/fixtures/v2/apps/PUT_{id}_routes_{route_id}_response.json rename to tests/fixtures/v2/apps/PUT_{id}_routes_{route_id}_response.json diff --git a/test/fixtures/v2/buildpacks/GET_response.json b/tests/fixtures/v2/buildpacks/GET_response.json similarity index 100% rename from test/fixtures/v2/buildpacks/GET_response.json rename to tests/fixtures/v2/buildpacks/GET_response.json diff --git a/test/fixtures/v2/buildpacks/GET_{id}_response.json b/tests/fixtures/v2/buildpacks/GET_{id}_response.json similarity index 100% rename from test/fixtures/v2/buildpacks/GET_{id}_response.json rename to tests/fixtures/v2/buildpacks/GET_{id}_response.json diff --git a/test/fixtures/v2/buildpacks/PUT_{id}_response.json b/tests/fixtures/v2/buildpacks/PUT_{id}_response.json similarity index 100% rename from test/fixtures/v2/buildpacks/PUT_{id}_response.json rename to tests/fixtures/v2/buildpacks/PUT_{id}_response.json diff --git a/test/fixtures/v2/events/GET_response_audit.route.delete-request.json b/tests/fixtures/v2/events/GET_response_audit.route.delete-request.json similarity index 100% rename from test/fixtures/v2/events/GET_response_audit.route.delete-request.json rename to tests/fixtures/v2/events/GET_response_audit.route.delete-request.json diff --git a/test/fixtures/v2/organizations/GET_response.json b/tests/fixtures/v2/organizations/GET_response.json similarity index 100% rename from test/fixtures/v2/organizations/GET_response.json rename to tests/fixtures/v2/organizations/GET_response.json diff --git a/test/fixtures/v2/organizations/GET_{id}_response.json b/tests/fixtures/v2/organizations/GET_{id}_response.json similarity index 100% rename from test/fixtures/v2/organizations/GET_{id}_response.json rename to tests/fixtures/v2/organizations/GET_{id}_response.json diff --git a/test/fixtures/v2/routes/GET_response.json b/tests/fixtures/v2/routes/GET_response.json similarity index 100% rename from test/fixtures/v2/routes/GET_response.json rename to tests/fixtures/v2/routes/GET_response.json diff --git a/test/fixtures/v2/routes/GET_{id}_response.json b/tests/fixtures/v2/routes/GET_{id}_response.json similarity index 100% rename from test/fixtures/v2/routes/GET_{id}_response.json rename to tests/fixtures/v2/routes/GET_{id}_response.json diff --git a/test/fixtures/v2/service_bindings/GET_response.json b/tests/fixtures/v2/service_bindings/GET_response.json similarity index 100% rename from test/fixtures/v2/service_bindings/GET_response.json rename to tests/fixtures/v2/service_bindings/GET_response.json diff --git a/test/fixtures/v2/service_bindings/GET_{id}_response.json b/tests/fixtures/v2/service_bindings/GET_{id}_response.json similarity index 100% rename from test/fixtures/v2/service_bindings/GET_{id}_response.json rename to tests/fixtures/v2/service_bindings/GET_{id}_response.json diff --git a/test/fixtures/v2/service_bindings/POST_response.json b/tests/fixtures/v2/service_bindings/POST_response.json similarity index 100% rename from test/fixtures/v2/service_bindings/POST_response.json rename to tests/fixtures/v2/service_bindings/POST_response.json diff --git a/test/fixtures/v2/service_brokers/GET_response.json b/tests/fixtures/v2/service_brokers/GET_response.json similarity index 100% rename from test/fixtures/v2/service_brokers/GET_response.json rename to tests/fixtures/v2/service_brokers/GET_response.json diff --git a/test/fixtures/v2/service_brokers/GET_{id}_response.json b/tests/fixtures/v2/service_brokers/GET_{id}_response.json similarity index 100% rename from test/fixtures/v2/service_brokers/GET_{id}_response.json rename to tests/fixtures/v2/service_brokers/GET_{id}_response.json diff --git a/test/fixtures/v2/service_brokers/POST_response.json b/tests/fixtures/v2/service_brokers/POST_response.json similarity index 100% rename from test/fixtures/v2/service_brokers/POST_response.json rename to tests/fixtures/v2/service_brokers/POST_response.json diff --git a/test/fixtures/v2/service_brokers/PUT_{id}_response.json b/tests/fixtures/v2/service_brokers/PUT_{id}_response.json similarity index 100% rename from test/fixtures/v2/service_brokers/PUT_{id}_response.json rename to tests/fixtures/v2/service_brokers/PUT_{id}_response.json diff --git a/test/fixtures/v2/service_instances/GET_response.json b/tests/fixtures/v2/service_instances/GET_response.json similarity index 100% rename from test/fixtures/v2/service_instances/GET_response.json rename to tests/fixtures/v2/service_instances/GET_response.json diff --git a/test/fixtures/v2/service_instances/GET_{id}_response.json b/tests/fixtures/v2/service_instances/GET_{id}_response.json similarity index 100% rename from test/fixtures/v2/service_instances/GET_{id}_response.json rename to tests/fixtures/v2/service_instances/GET_{id}_response.json diff --git a/test/fixtures/v2/service_instances/POST_response.json b/tests/fixtures/v2/service_instances/POST_response.json similarity index 100% rename from test/fixtures/v2/service_instances/POST_response.json rename to tests/fixtures/v2/service_instances/POST_response.json diff --git a/test/fixtures/v2/service_instances/PUT_{id}_response.json b/tests/fixtures/v2/service_instances/PUT_{id}_response.json similarity index 100% rename from test/fixtures/v2/service_instances/PUT_{id}_response.json rename to tests/fixtures/v2/service_instances/PUT_{id}_response.json diff --git a/test/fixtures/v2/service_keys/GET_response.json b/tests/fixtures/v2/service_keys/GET_response.json similarity index 100% rename from test/fixtures/v2/service_keys/GET_response.json rename to tests/fixtures/v2/service_keys/GET_response.json diff --git a/test/fixtures/v2/service_keys/GET_{id}_response.json b/tests/fixtures/v2/service_keys/GET_{id}_response.json similarity index 100% rename from test/fixtures/v2/service_keys/GET_{id}_response.json rename to tests/fixtures/v2/service_keys/GET_{id}_response.json diff --git a/test/fixtures/v2/service_keys/POST_response.json b/tests/fixtures/v2/service_keys/POST_response.json similarity index 100% rename from test/fixtures/v2/service_keys/POST_response.json rename to tests/fixtures/v2/service_keys/POST_response.json diff --git a/test/fixtures/v2/service_plan_visibilities/GET_response.json b/tests/fixtures/v2/service_plan_visibilities/GET_response.json similarity index 100% rename from test/fixtures/v2/service_plan_visibilities/GET_response.json rename to tests/fixtures/v2/service_plan_visibilities/GET_response.json diff --git a/test/fixtures/v2/service_plan_visibilities/GET_{id}_response.json b/tests/fixtures/v2/service_plan_visibilities/GET_{id}_response.json similarity index 100% rename from test/fixtures/v2/service_plan_visibilities/GET_{id}_response.json rename to tests/fixtures/v2/service_plan_visibilities/GET_{id}_response.json diff --git a/test/fixtures/v2/service_plan_visibilities/POST_response.json b/tests/fixtures/v2/service_plan_visibilities/POST_response.json similarity index 100% rename from test/fixtures/v2/service_plan_visibilities/POST_response.json rename to tests/fixtures/v2/service_plan_visibilities/POST_response.json diff --git a/test/fixtures/v2/service_plan_visibilities/PUT_{id}_response.json b/tests/fixtures/v2/service_plan_visibilities/PUT_{id}_response.json similarity index 100% rename from test/fixtures/v2/service_plan_visibilities/PUT_{id}_response.json rename to tests/fixtures/v2/service_plan_visibilities/PUT_{id}_response.json diff --git a/test/fixtures/v2/service_plans/GET_response.json b/tests/fixtures/v2/service_plans/GET_response.json similarity index 100% rename from test/fixtures/v2/service_plans/GET_response.json rename to tests/fixtures/v2/service_plans/GET_response.json diff --git a/test/fixtures/v2/service_plans/GET_{id}_response.json b/tests/fixtures/v2/service_plans/GET_{id}_response.json similarity index 100% rename from test/fixtures/v2/service_plans/GET_{id}_response.json rename to tests/fixtures/v2/service_plans/GET_{id}_response.json diff --git a/test/fixtures/v2/services/GET_response.json b/tests/fixtures/v2/services/GET_response.json similarity index 100% rename from test/fixtures/v2/services/GET_response.json rename to tests/fixtures/v2/services/GET_response.json diff --git a/test/fixtures/v2/services/GET_{id}_response.json b/tests/fixtures/v2/services/GET_{id}_response.json similarity index 100% rename from test/fixtures/v2/services/GET_{id}_response.json rename to tests/fixtures/v2/services/GET_{id}_response.json diff --git a/test/fixtures/v2/spaces/GET_response.json b/tests/fixtures/v2/spaces/GET_response.json similarity index 100% rename from test/fixtures/v2/spaces/GET_response.json rename to tests/fixtures/v2/spaces/GET_response.json diff --git a/test/fixtures/v2/spaces/GET_{id}_response.json b/tests/fixtures/v2/spaces/GET_{id}_response.json similarity index 100% rename from test/fixtures/v2/spaces/GET_{id}_response.json rename to tests/fixtures/v2/spaces/GET_{id}_response.json diff --git a/test/fixtures/v3/apps/GET_response.json b/tests/fixtures/v3/apps/GET_response.json similarity index 100% rename from test/fixtures/v3/apps/GET_response.json rename to tests/fixtures/v3/apps/GET_response.json diff --git a/test/fixtures/v3/apps/GET_response_include_space.json b/tests/fixtures/v3/apps/GET_response_include_space.json similarity index 100% rename from test/fixtures/v3/apps/GET_response_include_space.json rename to tests/fixtures/v3/apps/GET_response_include_space.json diff --git a/test/fixtures/v3/apps/GET_{id}_env_response.json b/tests/fixtures/v3/apps/GET_{id}_env_response.json similarity index 100% rename from test/fixtures/v3/apps/GET_{id}_env_response.json rename to tests/fixtures/v3/apps/GET_{id}_env_response.json diff --git a/test/fixtures/v3/apps/GET_{id}_environment_variables_response.json b/tests/fixtures/v3/apps/GET_{id}_environment_variables_response.json similarity index 100% rename from test/fixtures/v3/apps/GET_{id}_environment_variables_response.json rename to tests/fixtures/v3/apps/GET_{id}_environment_variables_response.json diff --git a/test/fixtures/v3/apps/GET_{id}_response.json b/tests/fixtures/v3/apps/GET_{id}_response.json similarity index 100% rename from test/fixtures/v3/apps/GET_{id}_response.json rename to tests/fixtures/v3/apps/GET_{id}_response.json diff --git a/test/fixtures/v3/apps/GET_{id}_response_include_space_and_org.json b/tests/fixtures/v3/apps/GET_{id}_response_include_space_and_org.json similarity index 100% rename from test/fixtures/v3/apps/GET_{id}_response_include_space_and_org.json rename to tests/fixtures/v3/apps/GET_{id}_response_include_space_and_org.json diff --git a/test/fixtures/v3/apps/GET_{id}_routes_response.json b/tests/fixtures/v3/apps/GET_{id}_routes_response.json similarity index 100% rename from test/fixtures/v3/apps/GET_{id}_routes_response.json rename to tests/fixtures/v3/apps/GET_{id}_routes_response.json diff --git a/test/fixtures/v3/apps/POST_{id}_actions_start_response.json b/tests/fixtures/v3/apps/POST_{id}_actions_start_response.json similarity index 100% rename from test/fixtures/v3/apps/POST_{id}_actions_start_response.json rename to tests/fixtures/v3/apps/POST_{id}_actions_start_response.json diff --git a/test/fixtures/v3/buildpacks/GET_response.json b/tests/fixtures/v3/buildpacks/GET_response.json similarity index 100% rename from test/fixtures/v3/buildpacks/GET_response.json rename to tests/fixtures/v3/buildpacks/GET_response.json diff --git a/test/fixtures/v3/buildpacks/GET_{id}_response.json b/tests/fixtures/v3/buildpacks/GET_{id}_response.json similarity index 100% rename from test/fixtures/v3/buildpacks/GET_{id}_response.json rename to tests/fixtures/v3/buildpacks/GET_{id}_response.json diff --git a/test/fixtures/v3/buildpacks/PATCH_{id}_response.json b/tests/fixtures/v3/buildpacks/PATCH_{id}_response.json similarity index 100% rename from test/fixtures/v3/buildpacks/PATCH_{id}_response.json rename to tests/fixtures/v3/buildpacks/PATCH_{id}_response.json diff --git a/test/fixtures/v3/buildpacks/POST_response.json b/tests/fixtures/v3/buildpacks/POST_response.json similarity index 100% rename from test/fixtures/v3/buildpacks/POST_response.json rename to tests/fixtures/v3/buildpacks/POST_response.json diff --git a/test/fixtures/v3/domains/GET_response.json b/tests/fixtures/v3/domains/GET_response.json similarity index 100% rename from test/fixtures/v3/domains/GET_response.json rename to tests/fixtures/v3/domains/GET_response.json diff --git a/test/fixtures/v3/domains/GET_{id}_response.json b/tests/fixtures/v3/domains/GET_{id}_response.json similarity index 100% rename from test/fixtures/v3/domains/GET_{id}_response.json rename to tests/fixtures/v3/domains/GET_{id}_response.json diff --git a/test/fixtures/v3/domains/PATCH_{id}_response.json b/tests/fixtures/v3/domains/PATCH_{id}_response.json similarity index 100% rename from test/fixtures/v3/domains/PATCH_{id}_response.json rename to tests/fixtures/v3/domains/PATCH_{id}_response.json diff --git a/test/fixtures/v3/domains/POST_response.json b/tests/fixtures/v3/domains/POST_response.json similarity index 100% rename from test/fixtures/v3/domains/POST_response.json rename to tests/fixtures/v3/domains/POST_response.json diff --git a/test/fixtures/v3/domains/POST_{id}_relationships_shared_organizations_response.json b/tests/fixtures/v3/domains/POST_{id}_relationships_shared_organizations_response.json similarity index 100% rename from test/fixtures/v3/domains/POST_{id}_relationships_shared_organizations_response.json rename to tests/fixtures/v3/domains/POST_{id}_relationships_shared_organizations_response.json diff --git a/test/fixtures/v3/feature_flags/GET_response.json b/tests/fixtures/v3/feature_flags/GET_response.json similarity index 100% rename from test/fixtures/v3/feature_flags/GET_response.json rename to tests/fixtures/v3/feature_flags/GET_response.json diff --git a/test/fixtures/v3/feature_flags/GET_{id}_response.json b/tests/fixtures/v3/feature_flags/GET_{id}_response.json similarity index 100% rename from test/fixtures/v3/feature_flags/GET_{id}_response.json rename to tests/fixtures/v3/feature_flags/GET_{id}_response.json diff --git a/test/fixtures/v3/feature_flags/PATCH_{id}_response.json b/tests/fixtures/v3/feature_flags/PATCH_{id}_response.json similarity index 100% rename from test/fixtures/v3/feature_flags/PATCH_{id}_response.json rename to tests/fixtures/v3/feature_flags/PATCH_{id}_response.json diff --git a/test/fixtures/v3/isolation_segments/GET_response.json b/tests/fixtures/v3/isolation_segments/GET_response.json similarity index 100% rename from test/fixtures/v3/isolation_segments/GET_response.json rename to tests/fixtures/v3/isolation_segments/GET_response.json diff --git a/test/fixtures/v3/isolation_segments/GET_{id}_relationships_organizations_response.json b/tests/fixtures/v3/isolation_segments/GET_{id}_relationships_organizations_response.json similarity index 100% rename from test/fixtures/v3/isolation_segments/GET_{id}_relationships_organizations_response.json rename to tests/fixtures/v3/isolation_segments/GET_{id}_relationships_organizations_response.json diff --git a/test/fixtures/v3/isolation_segments/GET_{id}_relationships_spaces_response.json b/tests/fixtures/v3/isolation_segments/GET_{id}_relationships_spaces_response.json similarity index 100% rename from test/fixtures/v3/isolation_segments/GET_{id}_relationships_spaces_response.json rename to tests/fixtures/v3/isolation_segments/GET_{id}_relationships_spaces_response.json diff --git a/test/fixtures/v3/isolation_segments/GET_{id}_response.json b/tests/fixtures/v3/isolation_segments/GET_{id}_response.json similarity index 100% rename from test/fixtures/v3/isolation_segments/GET_{id}_response.json rename to tests/fixtures/v3/isolation_segments/GET_{id}_response.json diff --git a/test/fixtures/v3/isolation_segments/PATCH_{id}_response.json b/tests/fixtures/v3/isolation_segments/PATCH_{id}_response.json similarity index 100% rename from test/fixtures/v3/isolation_segments/PATCH_{id}_response.json rename to tests/fixtures/v3/isolation_segments/PATCH_{id}_response.json diff --git a/test/fixtures/v3/isolation_segments/POST_response.json b/tests/fixtures/v3/isolation_segments/POST_response.json similarity index 100% rename from test/fixtures/v3/isolation_segments/POST_response.json rename to tests/fixtures/v3/isolation_segments/POST_response.json diff --git a/test/fixtures/v3/isolation_segments/POST_{id}_relationships_organizations_response.json b/tests/fixtures/v3/isolation_segments/POST_{id}_relationships_organizations_response.json similarity index 100% rename from test/fixtures/v3/isolation_segments/POST_{id}_relationships_organizations_response.json rename to tests/fixtures/v3/isolation_segments/POST_{id}_relationships_organizations_response.json diff --git a/test/fixtures/v3/jobs/GET_{id}_complete_response.json b/tests/fixtures/v3/jobs/GET_{id}_complete_response.json similarity index 100% rename from test/fixtures/v3/jobs/GET_{id}_complete_response.json rename to tests/fixtures/v3/jobs/GET_{id}_complete_response.json diff --git a/test/fixtures/v3/jobs/GET_{id}_failed_response.json b/tests/fixtures/v3/jobs/GET_{id}_failed_response.json similarity index 100% rename from test/fixtures/v3/jobs/GET_{id}_failed_response.json rename to tests/fixtures/v3/jobs/GET_{id}_failed_response.json diff --git a/test/fixtures/v3/jobs/GET_{id}_processing_response.json b/tests/fixtures/v3/jobs/GET_{id}_processing_response.json similarity index 100% rename from test/fixtures/v3/jobs/GET_{id}_processing_response.json rename to tests/fixtures/v3/jobs/GET_{id}_processing_response.json diff --git a/test/fixtures/v3/organization_quotas/GET_response.json b/tests/fixtures/v3/organization_quotas/GET_response.json similarity index 100% rename from test/fixtures/v3/organization_quotas/GET_response.json rename to tests/fixtures/v3/organization_quotas/GET_response.json diff --git a/test/fixtures/v3/organization_quotas/GET_{id}_response.json b/tests/fixtures/v3/organization_quotas/GET_{id}_response.json similarity index 100% rename from test/fixtures/v3/organization_quotas/GET_{id}_response.json rename to tests/fixtures/v3/organization_quotas/GET_{id}_response.json diff --git a/test/fixtures/v3/organization_quotas/PATCH_{id}_response.json b/tests/fixtures/v3/organization_quotas/PATCH_{id}_response.json similarity index 100% rename from test/fixtures/v3/organization_quotas/PATCH_{id}_response.json rename to tests/fixtures/v3/organization_quotas/PATCH_{id}_response.json diff --git a/test/fixtures/v3/organization_quotas/POST_response.json b/tests/fixtures/v3/organization_quotas/POST_response.json similarity index 100% rename from test/fixtures/v3/organization_quotas/POST_response.json rename to tests/fixtures/v3/organization_quotas/POST_response.json diff --git a/test/fixtures/v3/organization_quotas/POST_{id}_organizations_response.json b/tests/fixtures/v3/organization_quotas/POST_{id}_organizations_response.json similarity index 100% rename from test/fixtures/v3/organization_quotas/POST_{id}_organizations_response.json rename to tests/fixtures/v3/organization_quotas/POST_{id}_organizations_response.json diff --git a/test/fixtures/v3/organizations/GET_response.json b/tests/fixtures/v3/organizations/GET_response.json similarity index 100% rename from test/fixtures/v3/organizations/GET_response.json rename to tests/fixtures/v3/organizations/GET_response.json diff --git a/test/fixtures/v3/organizations/GET_{id}_default_domain_response.json b/tests/fixtures/v3/organizations/GET_{id}_default_domain_response.json similarity index 100% rename from test/fixtures/v3/organizations/GET_{id}_default_domain_response.json rename to tests/fixtures/v3/organizations/GET_{id}_default_domain_response.json diff --git a/test/fixtures/v3/organizations/GET_{id}_relationships_default_isolation_segment_response.json b/tests/fixtures/v3/organizations/GET_{id}_relationships_default_isolation_segment_response.json similarity index 100% rename from test/fixtures/v3/organizations/GET_{id}_relationships_default_isolation_segment_response.json rename to tests/fixtures/v3/organizations/GET_{id}_relationships_default_isolation_segment_response.json diff --git a/test/fixtures/v3/organizations/GET_{id}_response.json b/tests/fixtures/v3/organizations/GET_{id}_response.json similarity index 100% rename from test/fixtures/v3/organizations/GET_{id}_response.json rename to tests/fixtures/v3/organizations/GET_{id}_response.json diff --git a/test/fixtures/v3/organizations/GET_{id}_usage_summary_response.json b/tests/fixtures/v3/organizations/GET_{id}_usage_summary_response.json similarity index 100% rename from test/fixtures/v3/organizations/GET_{id}_usage_summary_response.json rename to tests/fixtures/v3/organizations/GET_{id}_usage_summary_response.json diff --git a/test/fixtures/v3/organizations/PATCH_{id}_relationships_default_isolation_segment_response.json b/tests/fixtures/v3/organizations/PATCH_{id}_relationships_default_isolation_segment_response.json similarity index 100% rename from test/fixtures/v3/organizations/PATCH_{id}_relationships_default_isolation_segment_response.json rename to tests/fixtures/v3/organizations/PATCH_{id}_relationships_default_isolation_segment_response.json diff --git a/test/fixtures/v3/organizations/PATCH_{id}_response.json b/tests/fixtures/v3/organizations/PATCH_{id}_response.json similarity index 100% rename from test/fixtures/v3/organizations/PATCH_{id}_response.json rename to tests/fixtures/v3/organizations/PATCH_{id}_response.json diff --git a/test/fixtures/v3/organizations/POST_response.json b/tests/fixtures/v3/organizations/POST_response.json similarity index 100% rename from test/fixtures/v3/organizations/POST_response.json rename to tests/fixtures/v3/organizations/POST_response.json diff --git a/test/fixtures/v3/processes/GET_response.json b/tests/fixtures/v3/processes/GET_response.json similarity index 100% rename from test/fixtures/v3/processes/GET_response.json rename to tests/fixtures/v3/processes/GET_response.json diff --git a/test/fixtures/v3/processes/GET_{id}_response.json b/tests/fixtures/v3/processes/GET_{id}_response.json similarity index 100% rename from test/fixtures/v3/processes/GET_{id}_response.json rename to tests/fixtures/v3/processes/GET_{id}_response.json diff --git a/test/fixtures/v3/processes/GET_{id}_stats_response.json b/tests/fixtures/v3/processes/GET_{id}_stats_response.json similarity index 100% rename from test/fixtures/v3/processes/GET_{id}_stats_response.json rename to tests/fixtures/v3/processes/GET_{id}_stats_response.json diff --git a/test/fixtures/v3/roles/GET_response.json b/tests/fixtures/v3/roles/GET_response.json similarity index 100% rename from test/fixtures/v3/roles/GET_response.json rename to tests/fixtures/v3/roles/GET_response.json diff --git a/test/fixtures/v3/roles/GET_{id}_response.json b/tests/fixtures/v3/roles/GET_{id}_response.json similarity index 100% rename from test/fixtures/v3/roles/GET_{id}_response.json rename to tests/fixtures/v3/roles/GET_{id}_response.json diff --git a/test/fixtures/v3/security_groups/GET_response.json b/tests/fixtures/v3/security_groups/GET_response.json similarity index 100% rename from test/fixtures/v3/security_groups/GET_response.json rename to tests/fixtures/v3/security_groups/GET_response.json diff --git a/test/fixtures/v3/security_groups/GET_{id}_response.json b/tests/fixtures/v3/security_groups/GET_{id}_response.json similarity index 100% rename from test/fixtures/v3/security_groups/GET_{id}_response.json rename to tests/fixtures/v3/security_groups/GET_{id}_response.json diff --git a/test/fixtures/v3/security_groups/PATCH_{id}_response.json b/tests/fixtures/v3/security_groups/PATCH_{id}_response.json similarity index 100% rename from test/fixtures/v3/security_groups/PATCH_{id}_response.json rename to tests/fixtures/v3/security_groups/PATCH_{id}_response.json diff --git a/test/fixtures/v3/security_groups/POST_response.json b/tests/fixtures/v3/security_groups/POST_response.json similarity index 100% rename from test/fixtures/v3/security_groups/POST_response.json rename to tests/fixtures/v3/security_groups/POST_response.json diff --git a/test/fixtures/v3/security_groups/POST_{id}_relationships_running_spaces_response.json b/tests/fixtures/v3/security_groups/POST_{id}_relationships_running_spaces_response.json similarity index 100% rename from test/fixtures/v3/security_groups/POST_{id}_relationships_running_spaces_response.json rename to tests/fixtures/v3/security_groups/POST_{id}_relationships_running_spaces_response.json diff --git a/test/fixtures/v3/security_groups/POST_{id}_relationships_staging_spaces_response.json b/tests/fixtures/v3/security_groups/POST_{id}_relationships_staging_spaces_response.json similarity index 100% rename from test/fixtures/v3/security_groups/POST_{id}_relationships_staging_spaces_response.json rename to tests/fixtures/v3/security_groups/POST_{id}_relationships_staging_spaces_response.json diff --git a/test/fixtures/v3/service_brokers/GET_response.json b/tests/fixtures/v3/service_brokers/GET_response.json similarity index 100% rename from test/fixtures/v3/service_brokers/GET_response.json rename to tests/fixtures/v3/service_brokers/GET_response.json diff --git a/test/fixtures/v3/service_brokers/GET_{id}_response.json b/tests/fixtures/v3/service_brokers/GET_{id}_response.json similarity index 100% rename from test/fixtures/v3/service_brokers/GET_{id}_response.json rename to tests/fixtures/v3/service_brokers/GET_{id}_response.json diff --git a/test/fixtures/v3/service_brokers/PATCH_{id}_response.json b/tests/fixtures/v3/service_brokers/PATCH_{id}_response.json similarity index 100% rename from test/fixtures/v3/service_brokers/PATCH_{id}_response.json rename to tests/fixtures/v3/service_brokers/PATCH_{id}_response.json diff --git a/test/fixtures/v3/service_brokers/POST_response.json b/tests/fixtures/v3/service_brokers/POST_response.json similarity index 100% rename from test/fixtures/v3/service_brokers/POST_response.json rename to tests/fixtures/v3/service_brokers/POST_response.json diff --git a/test/fixtures/v3/service_credential_bindings/GET_response.json b/tests/fixtures/v3/service_credential_bindings/GET_response.json similarity index 100% rename from test/fixtures/v3/service_credential_bindings/GET_response.json rename to tests/fixtures/v3/service_credential_bindings/GET_response.json diff --git a/test/fixtures/v3/service_credential_bindings/GET_{id}_details_response.json b/tests/fixtures/v3/service_credential_bindings/GET_{id}_details_response.json similarity index 100% rename from test/fixtures/v3/service_credential_bindings/GET_{id}_details_response.json rename to tests/fixtures/v3/service_credential_bindings/GET_{id}_details_response.json diff --git a/test/fixtures/v3/service_credential_bindings/GET_{id}_parameters_response.json b/tests/fixtures/v3/service_credential_bindings/GET_{id}_parameters_response.json similarity index 100% rename from test/fixtures/v3/service_credential_bindings/GET_{id}_parameters_response.json rename to tests/fixtures/v3/service_credential_bindings/GET_{id}_parameters_response.json diff --git a/test/fixtures/v3/service_credential_bindings/GET_{id}_response.json b/tests/fixtures/v3/service_credential_bindings/GET_{id}_response.json similarity index 100% rename from test/fixtures/v3/service_credential_bindings/GET_{id}_response.json rename to tests/fixtures/v3/service_credential_bindings/GET_{id}_response.json diff --git a/test/fixtures/v3/service_credential_bindings/POST_response.json b/tests/fixtures/v3/service_credential_bindings/POST_response.json similarity index 100% rename from test/fixtures/v3/service_credential_bindings/POST_response.json rename to tests/fixtures/v3/service_credential_bindings/POST_response.json diff --git a/test/fixtures/v3/service_instances/GET_response.json b/tests/fixtures/v3/service_instances/GET_response.json similarity index 100% rename from test/fixtures/v3/service_instances/GET_response.json rename to tests/fixtures/v3/service_instances/GET_response.json diff --git a/test/fixtures/v3/service_instances/GET_{id}_credentials_response.json b/tests/fixtures/v3/service_instances/GET_{id}_credentials_response.json similarity index 100% rename from test/fixtures/v3/service_instances/GET_{id}_credentials_response.json rename to tests/fixtures/v3/service_instances/GET_{id}_credentials_response.json diff --git a/test/fixtures/v3/service_instances/GET_{id}_permissions_response.json b/tests/fixtures/v3/service_instances/GET_{id}_permissions_response.json similarity index 100% rename from test/fixtures/v3/service_instances/GET_{id}_permissions_response.json rename to tests/fixtures/v3/service_instances/GET_{id}_permissions_response.json diff --git a/test/fixtures/v3/service_instances/GET_{id}_response.json b/tests/fixtures/v3/service_instances/GET_{id}_response.json similarity index 100% rename from test/fixtures/v3/service_instances/GET_{id}_response.json rename to tests/fixtures/v3/service_instances/GET_{id}_response.json diff --git a/test/fixtures/v3/service_offerings/GET_response.json b/tests/fixtures/v3/service_offerings/GET_response.json similarity index 100% rename from test/fixtures/v3/service_offerings/GET_response.json rename to tests/fixtures/v3/service_offerings/GET_response.json diff --git a/test/fixtures/v3/service_offerings/GET_{id}_response.json b/tests/fixtures/v3/service_offerings/GET_{id}_response.json similarity index 100% rename from test/fixtures/v3/service_offerings/GET_{id}_response.json rename to tests/fixtures/v3/service_offerings/GET_{id}_response.json diff --git a/test/fixtures/v3/service_offerings/PATCH_{id}_response.json b/tests/fixtures/v3/service_offerings/PATCH_{id}_response.json similarity index 100% rename from test/fixtures/v3/service_offerings/PATCH_{id}_response.json rename to tests/fixtures/v3/service_offerings/PATCH_{id}_response.json diff --git a/test/fixtures/v3/service_plans/GET_response.json b/tests/fixtures/v3/service_plans/GET_response.json similarity index 100% rename from test/fixtures/v3/service_plans/GET_response.json rename to tests/fixtures/v3/service_plans/GET_response.json diff --git a/test/fixtures/v3/service_plans/GET_{id}_response.json b/tests/fixtures/v3/service_plans/GET_{id}_response.json similarity index 100% rename from test/fixtures/v3/service_plans/GET_{id}_response.json rename to tests/fixtures/v3/service_plans/GET_{id}_response.json diff --git a/test/fixtures/v3/service_plans/GET_{id}_visibility_response.json b/tests/fixtures/v3/service_plans/GET_{id}_visibility_response.json similarity index 100% rename from test/fixtures/v3/service_plans/GET_{id}_visibility_response.json rename to tests/fixtures/v3/service_plans/GET_{id}_visibility_response.json diff --git a/test/fixtures/v3/service_plans/PATCH_{id}_response.json b/tests/fixtures/v3/service_plans/PATCH_{id}_response.json similarity index 100% rename from test/fixtures/v3/service_plans/PATCH_{id}_response.json rename to tests/fixtures/v3/service_plans/PATCH_{id}_response.json diff --git a/test/fixtures/v3/service_plans/PATCH_{id}_visibility_organizations_response.json b/tests/fixtures/v3/service_plans/PATCH_{id}_visibility_organizations_response.json similarity index 100% rename from test/fixtures/v3/service_plans/PATCH_{id}_visibility_organizations_response.json rename to tests/fixtures/v3/service_plans/PATCH_{id}_visibility_organizations_response.json diff --git a/test/fixtures/v3/service_plans/PATCH_{id}_visibility_type_response.json b/tests/fixtures/v3/service_plans/PATCH_{id}_visibility_type_response.json similarity index 100% rename from test/fixtures/v3/service_plans/PATCH_{id}_visibility_type_response.json rename to tests/fixtures/v3/service_plans/PATCH_{id}_visibility_type_response.json diff --git a/test/fixtures/v3/service_plans/POST_{id}_visibility_response.json b/tests/fixtures/v3/service_plans/POST_{id}_visibility_response.json similarity index 100% rename from test/fixtures/v3/service_plans/POST_{id}_visibility_response.json rename to tests/fixtures/v3/service_plans/POST_{id}_visibility_response.json diff --git a/test/fixtures/v3/spaces/GET_response.json b/tests/fixtures/v3/spaces/GET_response.json similarity index 100% rename from test/fixtures/v3/spaces/GET_response.json rename to tests/fixtures/v3/spaces/GET_response.json diff --git a/test/fixtures/v3/spaces/GET_response_include_org.json b/tests/fixtures/v3/spaces/GET_response_include_org.json similarity index 100% rename from test/fixtures/v3/spaces/GET_response_include_org.json rename to tests/fixtures/v3/spaces/GET_response_include_org.json diff --git a/test/fixtures/v3/spaces/GET_{id}_relationships_isolation_segment_response.json b/tests/fixtures/v3/spaces/GET_{id}_relationships_isolation_segment_response.json similarity index 100% rename from test/fixtures/v3/spaces/GET_{id}_relationships_isolation_segment_response.json rename to tests/fixtures/v3/spaces/GET_{id}_relationships_isolation_segment_response.json diff --git a/test/fixtures/v3/spaces/GET_{id}_response.json b/tests/fixtures/v3/spaces/GET_{id}_response.json similarity index 100% rename from test/fixtures/v3/spaces/GET_{id}_response.json rename to tests/fixtures/v3/spaces/GET_{id}_response.json diff --git a/test/fixtures/v3/spaces/GET_{id}_response_include_org.json b/tests/fixtures/v3/spaces/GET_{id}_response_include_org.json similarity index 100% rename from test/fixtures/v3/spaces/GET_{id}_response_include_org.json rename to tests/fixtures/v3/spaces/GET_{id}_response_include_org.json diff --git a/test/fixtures/v3/spaces/POST_response.json b/tests/fixtures/v3/spaces/POST_response.json similarity index 100% rename from test/fixtures/v3/spaces/POST_response.json rename to tests/fixtures/v3/spaces/POST_response.json diff --git a/test/fixtures/v3/spaces/POST_{id}_relationships_isolation_segment_response.json b/tests/fixtures/v3/spaces/POST_{id}_relationships_isolation_segment_response.json similarity index 100% rename from test/fixtures/v3/spaces/POST_{id}_relationships_isolation_segment_response.json rename to tests/fixtures/v3/spaces/POST_{id}_relationships_isolation_segment_response.json diff --git a/test/fixtures/v3/tasks/GET_response.json b/tests/fixtures/v3/tasks/GET_response.json similarity index 100% rename from test/fixtures/v3/tasks/GET_response.json rename to tests/fixtures/v3/tasks/GET_response.json diff --git a/test/fixtures/v3/tasks/GET_{id}_response.json b/tests/fixtures/v3/tasks/GET_{id}_response.json similarity index 100% rename from test/fixtures/v3/tasks/GET_{id}_response.json rename to tests/fixtures/v3/tasks/GET_{id}_response.json diff --git a/test/fixtures/v3/tasks/POST_response.json b/tests/fixtures/v3/tasks/POST_response.json similarity index 100% rename from test/fixtures/v3/tasks/POST_response.json rename to tests/fixtures/v3/tasks/POST_response.json diff --git a/test/fixtures/v3/tasks/POST_{id}_actions_cancel_response.json b/tests/fixtures/v3/tasks/POST_{id}_actions_cancel_response.json similarity index 100% rename from test/fixtures/v3/tasks/POST_{id}_actions_cancel_response.json rename to tests/fixtures/v3/tasks/POST_{id}_actions_cancel_response.json diff --git a/test/networking/__init__.py b/tests/networking/__init__.py similarity index 100% rename from test/networking/__init__.py rename to tests/networking/__init__.py diff --git a/test/networking/v1/__init__.py b/tests/networking/v1/__init__.py similarity index 100% rename from test/networking/v1/__init__.py rename to tests/networking/v1/__init__.py diff --git a/test/networking/v1/external/__init__.py b/tests/networking/v1/external/__init__.py similarity index 100% rename from test/networking/v1/external/__init__.py rename to tests/networking/v1/external/__init__.py diff --git a/test/networking/v1/external/test_policies.py b/tests/networking/v1/external/test_policies.py similarity index 100% rename from test/networking/v1/external/test_policies.py rename to tests/networking/v1/external/test_policies.py diff --git a/test/operations/__init__.py b/tests/operations/__init__.py similarity index 100% rename from test/operations/__init__.py rename to tests/operations/__init__.py diff --git a/test/operations/push/__init__.py b/tests/operations/push/__init__.py similarity index 100% rename from test/operations/push/__init__.py rename to tests/operations/push/__init__.py diff --git a/test/operations/push/test_cf_ignore.py b/tests/operations/push/test_cf_ignore.py similarity index 100% rename from test/operations/push/test_cf_ignore.py rename to tests/operations/push/test_cf_ignore.py diff --git a/test/operations/push/test_file_helper.py b/tests/operations/push/test_file_helper.py similarity index 100% rename from test/operations/push/test_file_helper.py rename to tests/operations/push/test_file_helper.py diff --git a/test/operations/push/test_push.py b/tests/operations/push/test_push.py similarity index 100% rename from test/operations/push/test_push.py rename to tests/operations/push/test_push.py diff --git a/test/operations/push/validation/__init__.py b/tests/operations/push/validation/__init__.py similarity index 100% rename from test/operations/push/validation/__init__.py rename to tests/operations/push/validation/__init__.py diff --git a/test/operations/push/validation/test_manifest_reader.py b/tests/operations/push/validation/test_manifest_reader.py similarity index 100% rename from test/operations/push/validation/test_manifest_reader.py rename to tests/operations/push/validation/test_manifest_reader.py diff --git a/test/test_client.py b/tests/test_client.py similarity index 100% rename from test/test_client.py rename to tests/test_client.py diff --git a/test/test_request_object.py b/tests/test_request_object.py similarity index 100% rename from test/test_request_object.py rename to tests/test_request_object.py diff --git a/test/v2/__init__.py b/tests/v2/__init__.py similarity index 100% rename from test/v2/__init__.py rename to tests/v2/__init__.py diff --git a/test/v2/test_apps.py b/tests/v2/test_apps.py similarity index 100% rename from test/v2/test_apps.py rename to tests/v2/test_apps.py diff --git a/test/v2/test_buildpacks.py b/tests/v2/test_buildpacks.py similarity index 100% rename from test/v2/test_buildpacks.py rename to tests/v2/test_buildpacks.py diff --git a/test/v2/test_doppler.py b/tests/v2/test_doppler.py similarity index 100% rename from test/v2/test_doppler.py rename to tests/v2/test_doppler.py diff --git a/test/v2/test_entities.py b/tests/v2/test_entities.py similarity index 100% rename from test/v2/test_entities.py rename to tests/v2/test_entities.py diff --git a/test/v2/test_events.py b/tests/v2/test_events.py similarity index 100% rename from test/v2/test_events.py rename to tests/v2/test_events.py diff --git a/test/v2/test_organizations.py b/tests/v2/test_organizations.py similarity index 100% rename from test/v2/test_organizations.py rename to tests/v2/test_organizations.py diff --git a/test/v2/test_routes.py b/tests/v2/test_routes.py similarity index 100% rename from test/v2/test_routes.py rename to tests/v2/test_routes.py diff --git a/test/v2/test_service_bindings.py b/tests/v2/test_service_bindings.py similarity index 100% rename from test/v2/test_service_bindings.py rename to tests/v2/test_service_bindings.py diff --git a/test/v2/test_service_brokers.py b/tests/v2/test_service_brokers.py similarity index 100% rename from test/v2/test_service_brokers.py rename to tests/v2/test_service_brokers.py diff --git a/test/v2/test_service_instances.py b/tests/v2/test_service_instances.py similarity index 100% rename from test/v2/test_service_instances.py rename to tests/v2/test_service_instances.py diff --git a/test/v2/test_service_keys.py b/tests/v2/test_service_keys.py similarity index 100% rename from test/v2/test_service_keys.py rename to tests/v2/test_service_keys.py diff --git a/test/v2/test_service_plan_visibilities.py b/tests/v2/test_service_plan_visibilities.py similarity index 100% rename from test/v2/test_service_plan_visibilities.py rename to tests/v2/test_service_plan_visibilities.py diff --git a/test/v2/test_service_plans.py b/tests/v2/test_service_plans.py similarity index 100% rename from test/v2/test_service_plans.py rename to tests/v2/test_service_plans.py diff --git a/test/v2/test_services.py b/tests/v2/test_services.py similarity index 100% rename from test/v2/test_services.py rename to tests/v2/test_services.py diff --git a/test/v2/test_spaces.py b/tests/v2/test_spaces.py similarity index 100% rename from test/v2/test_spaces.py rename to tests/v2/test_spaces.py diff --git a/test/v3/__init__.py b/tests/v3/__init__.py similarity index 100% rename from test/v3/__init__.py rename to tests/v3/__init__.py diff --git a/test/v3/test_apps.py b/tests/v3/test_apps.py similarity index 100% rename from test/v3/test_apps.py rename to tests/v3/test_apps.py diff --git a/test/v3/test_buildpacks.py b/tests/v3/test_buildpacks.py similarity index 100% rename from test/v3/test_buildpacks.py rename to tests/v3/test_buildpacks.py diff --git a/test/v3/test_domains.py b/tests/v3/test_domains.py similarity index 100% rename from test/v3/test_domains.py rename to tests/v3/test_domains.py diff --git a/test/v3/test_entities.py b/tests/v3/test_entities.py similarity index 100% rename from test/v3/test_entities.py rename to tests/v3/test_entities.py diff --git a/test/v3/test_feature_flags.py b/tests/v3/test_feature_flags.py similarity index 100% rename from test/v3/test_feature_flags.py rename to tests/v3/test_feature_flags.py diff --git a/test/v3/test_isolation_segments.py b/tests/v3/test_isolation_segments.py similarity index 100% rename from test/v3/test_isolation_segments.py rename to tests/v3/test_isolation_segments.py diff --git a/test/v3/test_jobs.py b/tests/v3/test_jobs.py similarity index 100% rename from test/v3/test_jobs.py rename to tests/v3/test_jobs.py diff --git a/test/v3/test_organization_quotas.py b/tests/v3/test_organization_quotas.py similarity index 100% rename from test/v3/test_organization_quotas.py rename to tests/v3/test_organization_quotas.py diff --git a/test/v3/test_organizations.py b/tests/v3/test_organizations.py similarity index 100% rename from test/v3/test_organizations.py rename to tests/v3/test_organizations.py diff --git a/test/v3/test_processes.py b/tests/v3/test_processes.py similarity index 100% rename from test/v3/test_processes.py rename to tests/v3/test_processes.py diff --git a/test/v3/test_roles.py b/tests/v3/test_roles.py similarity index 100% rename from test/v3/test_roles.py rename to tests/v3/test_roles.py diff --git a/test/v3/test_security_groups.py b/tests/v3/test_security_groups.py similarity index 100% rename from test/v3/test_security_groups.py rename to tests/v3/test_security_groups.py diff --git a/test/v3/test_service_brokers.py b/tests/v3/test_service_brokers.py similarity index 100% rename from test/v3/test_service_brokers.py rename to tests/v3/test_service_brokers.py diff --git a/test/v3/test_service_credential_bindings.py b/tests/v3/test_service_credential_bindings.py similarity index 100% rename from test/v3/test_service_credential_bindings.py rename to tests/v3/test_service_credential_bindings.py diff --git a/test/v3/test_service_instances.py b/tests/v3/test_service_instances.py similarity index 100% rename from test/v3/test_service_instances.py rename to tests/v3/test_service_instances.py diff --git a/test/v3/test_service_offerings.py b/tests/v3/test_service_offerings.py similarity index 100% rename from test/v3/test_service_offerings.py rename to tests/v3/test_service_offerings.py diff --git a/test/v3/test_service_plans.py b/tests/v3/test_service_plans.py similarity index 100% rename from test/v3/test_service_plans.py rename to tests/v3/test_service_plans.py diff --git a/test/v3/test_spaces.py b/tests/v3/test_spaces.py similarity index 100% rename from test/v3/test_spaces.py rename to tests/v3/test_spaces.py diff --git a/test/v3/test_tasks.py b/tests/v3/test_tasks.py similarity index 100% rename from test/v3/test_tasks.py rename to tests/v3/test_tasks.py From df2620df7b20038fdcb6ea84b8045497a3401f4f Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Fri, 12 Jul 2024 13:28:35 +0200 Subject: [PATCH 182/264] features(ci) go to poetry --- .flake8 | 2 +- .github/workflows/pythonpackage.yml | 21 +- dev-requirements.txt | 3 - poetry.lock | 1121 +++++++++++++++++++++++++++ poetry.toml | 5 + pyproject.toml | 41 +- requirements.txt | 1 - 7 files changed, 1180 insertions(+), 14 deletions(-) delete mode 100644 dev-requirements.txt create mode 100644 poetry.lock create mode 100644 poetry.toml diff --git a/.flake8 b/.flake8 index 8f714bc..358025e 100644 --- a/.flake8 +++ b/.flake8 @@ -3,5 +3,5 @@ ignore = E203, W503 max-line-length = 130 max-complexity = 16 buildins = CloudFoundryClient -exclude = vendors,.git,.github,main/cloudfoundry_client/dropsonde,venv,.eggs,build +exclude = vendors,.git,.github,cloudfoundry_client/dropsonde,.venv,.eggs,build diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index f676ae3..4be11e9 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -13,7 +13,7 @@ jobs: strategy: max-parallel: 1 matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 @@ -23,14 +23,19 @@ jobs: with: python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - pip install -r dev-requirements.txt + - name: Install and configure Poetry + uses: snok/install-poetry@v1 + with: + virtual envs-create: true + virtualenvs-in-project: true + virtualenvs-path: .venv + installer-parallel: true + + - name: Install project + run: poetry install --no-interaction - name: Launch tests - run: python setup.py test + run: poetry run pytest - name: Launch Linting - run: flake8 --show-source --statistics + run: poetry run flake8 --show-source --statistics diff --git a/dev-requirements.txt b/dev-requirements.txt deleted file mode 100644 index cfbd04a..0000000 --- a/dev-requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -black==24.4.2 -flake8==7.1.0 -setuptools; python_version >= '3.12' \ No newline at end of file diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..c35a13c --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1121 @@ +# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. + +[[package]] +name = "aiohappyeyeballs" +version = "2.4.4" +description = "Happy Eyeballs for asyncio" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiohappyeyeballs-2.4.4-py3-none-any.whl", hash = "sha256:a980909d50efcd44795c4afeca523296716d50cd756ddca6af8c65b996e27de8"}, + {file = "aiohappyeyeballs-2.4.4.tar.gz", hash = "sha256:5fdd7d87889c63183afc18ce9271f9b0a7d32c2303e394468dd45d514a757745"}, +] + +[[package]] +name = "aiohttp" +version = "3.11.11" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.9" +files = [ + {file = "aiohttp-3.11.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a60804bff28662cbcf340a4d61598891f12eea3a66af48ecfdc975ceec21e3c8"}, + {file = "aiohttp-3.11.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b4fa1cb5f270fb3eab079536b764ad740bb749ce69a94d4ec30ceee1b5940d5"}, + {file = "aiohttp-3.11.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:731468f555656767cda219ab42e033355fe48c85fbe3ba83a349631541715ba2"}, + {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb23d8bb86282b342481cad4370ea0853a39e4a32a0042bb52ca6bdde132df43"}, + {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f047569d655f81cb70ea5be942ee5d4421b6219c3f05d131f64088c73bb0917f"}, + {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd7659baae9ccf94ae5fe8bfaa2c7bc2e94d24611528395ce88d009107e00c6d"}, + {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af01e42ad87ae24932138f154105e88da13ce7d202a6de93fafdafb2883a00ef"}, + {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5854be2f3e5a729800bac57a8d76af464e160f19676ab6aea74bde18ad19d438"}, + {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6526e5fb4e14f4bbf30411216780c9967c20c5a55f2f51d3abd6de68320cc2f3"}, + {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:85992ee30a31835fc482468637b3e5bd085fa8fe9392ba0bdcbdc1ef5e9e3c55"}, + {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:88a12ad8ccf325a8a5ed80e6d7c3bdc247d66175afedbe104ee2aaca72960d8e"}, + {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0a6d3fbf2232e3a08c41eca81ae4f1dff3d8f1a30bae415ebe0af2d2458b8a33"}, + {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:84a585799c58b795573c7fa9b84c455adf3e1d72f19a2bf498b54a95ae0d194c"}, + {file = "aiohttp-3.11.11-cp310-cp310-win32.whl", hash = "sha256:bfde76a8f430cf5c5584553adf9926534352251d379dcb266ad2b93c54a29745"}, + {file = "aiohttp-3.11.11-cp310-cp310-win_amd64.whl", hash = "sha256:0fd82b8e9c383af11d2b26f27a478640b6b83d669440c0a71481f7c865a51da9"}, + {file = "aiohttp-3.11.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ba74ec819177af1ef7f59063c6d35a214a8fde6f987f7661f4f0eecc468a8f76"}, + {file = "aiohttp-3.11.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4af57160800b7a815f3fe0eba9b46bf28aafc195555f1824555fa2cfab6c1538"}, + {file = "aiohttp-3.11.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffa336210cf9cd8ed117011085817d00abe4c08f99968deef0013ea283547204"}, + {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81b8fe282183e4a3c7a1b72f5ade1094ed1c6345a8f153506d114af5bf8accd9"}, + {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3af41686ccec6a0f2bdc66686dc0f403c41ac2089f80e2214a0f82d001052c03"}, + {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70d1f9dde0e5dd9e292a6d4d00058737052b01f3532f69c0c65818dac26dc287"}, + {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:249cc6912405917344192b9f9ea5cd5b139d49e0d2f5c7f70bdfaf6b4dbf3a2e"}, + {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0eb98d90b6690827dcc84c246811feeb4e1eea683c0eac6caed7549be9c84665"}, + {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec82bf1fda6cecce7f7b915f9196601a1bd1a3079796b76d16ae4cce6d0ef89b"}, + {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9fd46ce0845cfe28f108888b3ab17abff84ff695e01e73657eec3f96d72eef34"}, + {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bd176afcf8f5d2aed50c3647d4925d0db0579d96f75a31e77cbaf67d8a87742d"}, + {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ec2aa89305006fba9ffb98970db6c8221541be7bee4c1d027421d6f6df7d1ce2"}, + {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:92cde43018a2e17d48bb09c79e4d4cb0e236de5063ce897a5e40ac7cb4878773"}, + {file = "aiohttp-3.11.11-cp311-cp311-win32.whl", hash = "sha256:aba807f9569455cba566882c8938f1a549f205ee43c27b126e5450dc9f83cc62"}, + {file = "aiohttp-3.11.11-cp311-cp311-win_amd64.whl", hash = "sha256:ae545f31489548c87b0cced5755cfe5a5308d00407000e72c4fa30b19c3220ac"}, + {file = "aiohttp-3.11.11-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e595c591a48bbc295ebf47cb91aebf9bd32f3ff76749ecf282ea7f9f6bb73886"}, + {file = "aiohttp-3.11.11-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3ea1b59dc06396b0b424740a10a0a63974c725b1c64736ff788a3689d36c02d2"}, + {file = "aiohttp-3.11.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8811f3f098a78ffa16e0ea36dffd577eb031aea797cbdba81be039a4169e242c"}, + {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7227b87a355ce1f4bf83bfae4399b1f5bb42e0259cb9405824bd03d2f4336a"}, + {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d40f9da8cabbf295d3a9dae1295c69975b86d941bc20f0a087f0477fa0a66231"}, + {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffb3dc385f6bb1568aa974fe65da84723210e5d9707e360e9ecb51f59406cd2e"}, + {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8f5f7515f3552d899c61202d99dcb17d6e3b0de777900405611cd747cecd1b8"}, + {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3499c7ffbfd9c6a3d8d6a2b01c26639da7e43d47c7b4f788016226b1e711caa8"}, + {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8e2bf8029dbf0810c7bfbc3e594b51c4cc9101fbffb583a3923aea184724203c"}, + {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b6212a60e5c482ef90f2d788835387070a88d52cf6241d3916733c9176d39eab"}, + {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d119fafe7b634dbfa25a8c597718e69a930e4847f0b88e172744be24515140da"}, + {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:6fba278063559acc730abf49845d0e9a9e1ba74f85f0ee6efd5803f08b285853"}, + {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:92fc484e34b733704ad77210c7957679c5c3877bd1e6b6d74b185e9320cc716e"}, + {file = "aiohttp-3.11.11-cp312-cp312-win32.whl", hash = "sha256:9f5b3c1ed63c8fa937a920b6c1bec78b74ee09593b3f5b979ab2ae5ef60d7600"}, + {file = "aiohttp-3.11.11-cp312-cp312-win_amd64.whl", hash = "sha256:1e69966ea6ef0c14ee53ef7a3d68b564cc408121ea56c0caa2dc918c1b2f553d"}, + {file = "aiohttp-3.11.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:541d823548ab69d13d23730a06f97460f4238ad2e5ed966aaf850d7c369782d9"}, + {file = "aiohttp-3.11.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:929f3ed33743a49ab127c58c3e0a827de0664bfcda566108989a14068f820194"}, + {file = "aiohttp-3.11.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0882c2820fd0132240edbb4a51eb8ceb6eef8181db9ad5291ab3332e0d71df5f"}, + {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b63de12e44935d5aca7ed7ed98a255a11e5cb47f83a9fded7a5e41c40277d104"}, + {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa54f8ef31d23c506910c21163f22b124facb573bff73930735cf9fe38bf7dff"}, + {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a344d5dc18074e3872777b62f5f7d584ae4344cd6006c17ba12103759d407af3"}, + {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7fb429ab1aafa1f48578eb315ca45bd46e9c37de11fe45c7f5f4138091e2f1"}, + {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c341c7d868750e31961d6d8e60ff040fb9d3d3a46d77fd85e1ab8e76c3e9a5c4"}, + {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ed9ee95614a71e87f1a70bc81603f6c6760128b140bc4030abe6abaa988f1c3d"}, + {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:de8d38f1c2810fa2a4f1d995a2e9c70bb8737b18da04ac2afbf3971f65781d87"}, + {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a9b7371665d4f00deb8f32208c7c5e652059b0fda41cf6dbcac6114a041f1cc2"}, + {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:620598717fce1b3bd14dd09947ea53e1ad510317c85dda2c9c65b622edc96b12"}, + {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf8d9bfee991d8acc72d060d53860f356e07a50f0e0d09a8dfedea1c554dd0d5"}, + {file = "aiohttp-3.11.11-cp313-cp313-win32.whl", hash = "sha256:9d73ee3725b7a737ad86c2eac5c57a4a97793d9f442599bea5ec67ac9f4bdc3d"}, + {file = "aiohttp-3.11.11-cp313-cp313-win_amd64.whl", hash = "sha256:c7a06301c2fb096bdb0bd25fe2011531c1453b9f2c163c8031600ec73af1cc99"}, + {file = "aiohttp-3.11.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3e23419d832d969f659c208557de4a123e30a10d26e1e14b73431d3c13444c2e"}, + {file = "aiohttp-3.11.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:21fef42317cf02e05d3b09c028712e1d73a9606f02467fd803f7c1f39cc59add"}, + {file = "aiohttp-3.11.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1f21bb8d0235fc10c09ce1d11ffbd40fc50d3f08a89e4cf3a0c503dc2562247a"}, + {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1642eceeaa5ab6c9b6dfeaaa626ae314d808188ab23ae196a34c9d97efb68350"}, + {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2170816e34e10f2fd120f603e951630f8a112e1be3b60963a1f159f5699059a6"}, + {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8be8508d110d93061197fd2d6a74f7401f73b6d12f8822bbcd6d74f2b55d71b1"}, + {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4eed954b161e6b9b65f6be446ed448ed3921763cc432053ceb606f89d793927e"}, + {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6c9af134da4bc9b3bd3e6a70072509f295d10ee60c697826225b60b9959acdd"}, + {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:44167fc6a763d534a6908bdb2592269b4bf30a03239bcb1654781adf5e49caf1"}, + {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:479b8c6ebd12aedfe64563b85920525d05d394b85f166b7873c8bde6da612f9c"}, + {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:10b4ff0ad793d98605958089fabfa350e8e62bd5d40aa65cdc69d6785859f94e"}, + {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:b540bd67cfb54e6f0865ceccd9979687210d7ed1a1cc8c01f8e67e2f1e883d28"}, + {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1dac54e8ce2ed83b1f6b1a54005c87dfed139cf3f777fdc8afc76e7841101226"}, + {file = "aiohttp-3.11.11-cp39-cp39-win32.whl", hash = "sha256:568c1236b2fde93b7720f95a890741854c1200fba4a3471ff48b2934d2d93fd3"}, + {file = "aiohttp-3.11.11-cp39-cp39-win_amd64.whl", hash = "sha256:943a8b052e54dfd6439fd7989f67fc6a7f2138d0a2cf0a7de5f18aa4fe7eb3b1"}, + {file = "aiohttp-3.11.11.tar.gz", hash = "sha256:bb49c7f1e6ebf3821a42d81d494f538107610c3a705987f53068546b0e90303e"}, +] + +[package.dependencies] +aiohappyeyeballs = ">=2.3.0" +aiosignal = ">=1.1.2" +async-timeout = {version = ">=4.0,<6.0", markers = "python_version < \"3.11\""} +attrs = ">=17.3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +propcache = ">=0.2.0" +yarl = ">=1.17.0,<2.0" + +[package.extras] +speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] + +[[package]] +name = "aiosignal" +version = "1.3.2" +description = "aiosignal: a list of registered asynchronous callbacks" +optional = false +python-versions = ">=3.9" +files = [ + {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, + {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, +] + +[package.dependencies] +frozenlist = ">=1.1.0" + +[[package]] +name = "async-timeout" +version = "5.0.1" +description = "Timeout context manager for asyncio programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, + {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, +] + +[[package]] +name = "attrs" +version = "24.3.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.8" +files = [ + {file = "attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308"}, + {file = "attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff"}, +] + +[package.extras] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] + +[[package]] +name = "black" +version = "24.4.2" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-24.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce"}, + {file = "black-24.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021"}, + {file = "black-24.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063"}, + {file = "black-24.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96"}, + {file = "black-24.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474"}, + {file = "black-24.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c"}, + {file = "black-24.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb"}, + {file = "black-24.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1"}, + {file = "black-24.4.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d"}, + {file = "black-24.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04"}, + {file = "black-24.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc"}, + {file = "black-24.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0"}, + {file = "black-24.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7"}, + {file = "black-24.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94"}, + {file = "black-24.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8"}, + {file = "black-24.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c"}, + {file = "black-24.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1"}, + {file = "black-24.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741"}, + {file = "black-24.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e"}, + {file = "black-24.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7"}, + {file = "black-24.4.2-py3-none-any.whl", hash = "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c"}, + {file = "black-24.4.2.tar.gz", hash = "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "certifi" +version = "2024.12.14" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, + {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.1" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7" +files = [ + {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, + {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, + {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, +] + +[[package]] +name = "click" +version = "8.1.8" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, + {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "flake8" +version = "7.1.0" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-7.1.0-py2.py3-none-any.whl", hash = "sha256:2e416edcc62471a64cea09353f4e7bdba32aeb079b6e360554c659a122b1bc6a"}, + {file = "flake8-7.1.0.tar.gz", hash = "sha256:48a07b626b55236e0fb4784ee69a465fbf59d79eec1f5b4785c3d3bc57d17aa5"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.12.0,<2.13.0" +pyflakes = ">=3.2.0,<3.3.0" + +[[package]] +name = "frozenlist" +version = "1.5.0" +description = "A list-like structure which implements collections.abc.MutableSequence" +optional = false +python-versions = ">=3.8" +files = [ + {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a"}, + {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb"}, + {file = "frozenlist-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15538c0cbf0e4fa11d1e3a71f823524b0c46299aed6e10ebb4c2089abd8c3bec"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e79225373c317ff1e35f210dd5f1344ff31066ba8067c307ab60254cd3a78ad5"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9272fa73ca71266702c4c3e2d4a28553ea03418e591e377a03b8e3659d94fa76"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:498524025a5b8ba81695761d78c8dd7382ac0b052f34e66939c42df860b8ff17"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92b5278ed9d50fe610185ecd23c55d8b307d75ca18e94c0e7de328089ac5dcba"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f3c8c1dacd037df16e85227bac13cca58c30da836c6f936ba1df0c05d046d8d"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2ac49a9bedb996086057b75bf93538240538c6d9b38e57c82d51f75a73409d2"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e66cc454f97053b79c2ab09c17fbe3c825ea6b4de20baf1be28919460dd7877f"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3ba5f9a0dfed20337d3e966dc359784c9f96503674c2faf015f7fe8e96798c"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6321899477db90bdeb9299ac3627a6a53c7399c8cd58d25da094007402b039ab"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76e4753701248476e6286f2ef492af900ea67d9706a0155335a40ea21bf3b2f5"}, + {file = "frozenlist-1.5.0-cp310-cp310-win32.whl", hash = "sha256:977701c081c0241d0955c9586ffdd9ce44f7a7795df39b9151cd9a6fd0ce4cfb"}, + {file = "frozenlist-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:189f03b53e64144f90990d29a27ec4f7997d91ed3d01b51fa39d2dbe77540fd4"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fd74520371c3c4175142d02a976aee0b4cb4a7cc912a60586ffd8d5929979b30"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f3f7a0fbc219fb4455264cae4d9f01ad41ae6ee8524500f381de64ffaa077d5"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f47c9c9028f55a04ac254346e92977bf0f166c483c74b4232bee19a6697e4778"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0996c66760924da6e88922756d99b47512a71cfd45215f3570bf1e0b694c206a"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2fe128eb4edeabe11896cb6af88fca5346059f6c8d807e3b910069f39157869"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a8ea951bbb6cacd492e3948b8da8c502a3f814f5d20935aae74b5df2b19cf3d"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de537c11e4aa01d37db0d403b57bd6f0546e71a82347a97c6a9f0dcc532b3a45"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c2623347b933fcb9095841f1cc5d4ff0b278addd743e0e966cb3d460278840d"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cee6798eaf8b1416ef6909b06f7dc04b60755206bddc599f52232606e18179d3"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f5f9da7f5dbc00a604fe74aa02ae7c98bcede8a3b8b9666f9f86fc13993bc71a"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:90646abbc7a5d5c7c19461d2e3eeb76eb0b204919e6ece342feb6032c9325ae9"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bdac3c7d9b705d253b2ce370fde941836a5f8b3c5c2b8fd70940a3ea3af7f4f2"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03d33c2ddbc1816237a67f66336616416e2bbb6beb306e5f890f2eb22b959cdf"}, + {file = "frozenlist-1.5.0-cp311-cp311-win32.whl", hash = "sha256:237f6b23ee0f44066219dae14c70ae38a63f0440ce6750f868ee08775073f942"}, + {file = "frozenlist-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:0cc974cc93d32c42e7b0f6cf242a6bd941c57c61b618e78b6c0a96cb72788c1d"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f"}, + {file = "frozenlist-1.5.0-cp312-cp312-win32.whl", hash = "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8"}, + {file = "frozenlist-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03"}, + {file = "frozenlist-1.5.0-cp313-cp313-win32.whl", hash = "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c"}, + {file = "frozenlist-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:dd94994fc91a6177bfaafd7d9fd951bc8689b0a98168aa26b5f543868548d3ca"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0da8bbec082bf6bf18345b180958775363588678f64998c2b7609e34719b10"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73f2e31ea8dd7df61a359b731716018c2be196e5bb3b74ddba107f694fbd7604"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:828afae9f17e6de596825cf4228ff28fbdf6065974e5ac1410cecc22f699d2b3"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1577515d35ed5649d52ab4319db757bb881ce3b2b796d7283e6634d99ace307"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2150cc6305a2c2ab33299453e2968611dacb970d2283a14955923062c8d00b10"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a72b7a6e3cd2725eff67cd64c8f13335ee18fc3c7befc05aed043d24c7b9ccb9"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c16d2fa63e0800723139137d667e1056bee1a1cf7965153d2d104b62855e9b99"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:17dcc32fc7bda7ce5875435003220a457bcfa34ab7924a49a1c19f55b6ee185c"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:97160e245ea33d8609cd2b8fd997c850b56db147a304a262abc2b3be021a9171"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f1e6540b7fa044eee0bb5111ada694cf3dc15f2b0347ca125ee9ca984d5e9e6e"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:91d6c171862df0a6c61479d9724f22efb6109111017c87567cfeb7b5d1449fdf"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c1fac3e2ace2eb1052e9f7c7db480818371134410e1f5c55d65e8f3ac6d1407e"}, + {file = "frozenlist-1.5.0-cp38-cp38-win32.whl", hash = "sha256:b97f7b575ab4a8af9b7bc1d2ef7f29d3afee2226bd03ca3875c16451ad5a7723"}, + {file = "frozenlist-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:374ca2dabdccad8e2a76d40b1d037f5bd16824933bf7bcea3e59c891fd4a0923"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9bbcdfaf4af7ce002694a4e10a0159d5a8d20056a12b05b45cea944a4953f972"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1893f948bf6681733aaccf36c5232c231e3b5166d607c5fa77773611df6dc336"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2b5e23253bb709ef57a8e95e6ae48daa9ac5f265637529e4ce6b003a37b2621f"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f253985bb515ecd89629db13cb58d702035ecd8cfbca7d7a7e29a0e6d39af5f"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04a5c6babd5e8fb7d3c871dc8b321166b80e41b637c31a995ed844a6139942b6"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fe0f1c29ba24ba6ff6abf688cb0b7cf1efab6b6aa6adc55441773c252f7411"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:226d72559fa19babe2ccd920273e767c96a49b9d3d38badd7c91a0fdeda8ea08"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15b731db116ab3aedec558573c1a5eec78822b32292fe4f2f0345b7f697745c2"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:366d8f93e3edfe5a918c874702f78faac300209a4d5bf38352b2c1bdc07a766d"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1b96af8c582b94d381a1c1f51ffaedeb77c821c690ea5f01da3d70a487dd0a9b"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c03eff4a41bd4e38415cbed054bbaff4a075b093e2394b6915dca34a40d1e38b"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:50cf5e7ee9b98f22bdecbabf3800ae78ddcc26e4a435515fc72d97903e8488e0"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e76bfbc72353269c44e0bc2cfe171900fbf7f722ad74c9a7b638052afe6a00c"}, + {file = "frozenlist-1.5.0-cp39-cp39-win32.whl", hash = "sha256:666534d15ba8f0fda3f53969117383d5dc021266b3c1a42c9ec4855e4b58b9d3"}, + {file = "frozenlist-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:5c28f4b5dbef8a0d8aad0d4de24d1e9e981728628afaf4ea0792f5d0939372f0"}, + {file = "frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3"}, + {file = "frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817"}, +] + +[[package]] +name = "idna" +version = "3.10" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.6" +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "multidict" +version = "6.1.0" +description = "multidict implementation" +optional = false +python-versions = ">=3.8" +files = [ + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7"}, + {file = "multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0"}, + {file = "multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"}, + {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"}, + {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"}, + {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"}, + {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"}, + {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"}, + {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd"}, + {file = "multidict-6.1.0-cp38-cp38-win32.whl", hash = "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167"}, + {file = "multidict-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43"}, + {file = "multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada"}, + {file = "multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a"}, + {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"}, + {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""} + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "oauth2-client" +version = "1.4.2" +description = "A client library for OAuth2" +optional = false +python-versions = "*" +files = [ + {file = "oauth2-client-1.4.2.tar.gz", hash = "sha256:5381900448ff1ae762eb7c65c501002eac46bb5ca2f49477fdfeaf9e9969f284"}, + {file = "oauth2_client-1.4.2-py3-none-any.whl", hash = "sha256:7b938ba8166128a3c4c15ad23ca0c95a2468f8e8b6069d019ebc73360c15c7ca"}, +] + +[package.dependencies] +requests = ">=2.5.0" + +[[package]] +name = "packaging" +version = "24.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "polling2" +version = "0.5.0" +description = "Updated polling utility with many configurable options" +optional = false +python-versions = "*" +files = [ + {file = "polling2-0.5.0-py2.py3-none-any.whl", hash = "sha256:ad86d56fbd7502f0856cac2d0109d595c18fa6c7fb12c88cee5e5d16c17286c1"}, + {file = "polling2-0.5.0.tar.gz", hash = "sha256:90b7da82cf7adbb48029724d3546af93f21ab6e592ec37c8c4619aedd010e342"}, +] + +[[package]] +name = "propcache" +version = "0.2.1" +description = "Accelerated property cache" +optional = false +python-versions = ">=3.9" +files = [ + {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6"}, + {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2"}, + {file = "propcache-0.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6445804cf4ec763dc70de65a3b0d9954e868609e83850a47ca4f0cb64bd79fea"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9479aa06a793c5aeba49ce5c5692ffb51fcd9a7016e017d555d5e2b0045d212"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9631c5e8b5b3a0fda99cb0d29c18133bca1e18aea9effe55adb3da1adef80d3"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3156628250f46a0895f1f36e1d4fbe062a1af8718ec3ebeb746f1d23f0c5dc4d"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b6fb63ae352e13748289f04f37868099e69dba4c2b3e271c46061e82c745634"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:887d9b0a65404929641a9fabb6452b07fe4572b269d901d622d8a34a4e9043b2"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a96dc1fa45bd8c407a0af03b2d5218392729e1822b0c32e62c5bf7eeb5fb3958"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a7e65eb5c003a303b94aa2c3852ef130230ec79e349632d030e9571b87c4698c"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:999779addc413181912e984b942fbcc951be1f5b3663cd80b2687758f434c583"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:19a0f89a7bb9d8048d9c4370c9c543c396e894c76be5525f5e1ad287f1750ddf"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1ac2f5fe02fa75f56e1ad473f1175e11f475606ec9bd0be2e78e4734ad575034"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:574faa3b79e8ebac7cb1d7930f51184ba1ccf69adfdec53a12f319a06030a68b"}, + {file = "propcache-0.2.1-cp310-cp310-win32.whl", hash = "sha256:03ff9d3f665769b2a85e6157ac8b439644f2d7fd17615a82fa55739bc97863f4"}, + {file = "propcache-0.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:2d3af2e79991102678f53e0dbf4c35de99b6b8b58f29a27ca0325816364caaba"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ffc3cca89bb438fb9c95c13fc874012f7b9466b89328c3c8b1aa93cdcfadd16"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f174bbd484294ed9fdf09437f889f95807e5f229d5d93588d34e92106fbf6717"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:70693319e0b8fd35dd863e3e29513875eb15c51945bf32519ef52927ca883bc3"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b480c6a4e1138e1aa137c0079b9b6305ec6dcc1098a8ca5196283e8a49df95a9"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d27b84d5880f6d8aa9ae3edb253c59d9f6642ffbb2c889b78b60361eed449787"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:857112b22acd417c40fa4595db2fe28ab900c8c5fe4670c7989b1c0230955465"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf6c4150f8c0e32d241436526f3c3f9cbd34429492abddbada2ffcff506c51af"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66d4cfda1d8ed687daa4bc0274fcfd5267873db9a5bc0418c2da19273040eeb7"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2f992c07c0fca81655066705beae35fc95a2fa7366467366db627d9f2ee097f"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:4a571d97dbe66ef38e472703067021b1467025ec85707d57e78711c085984e54"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bb6178c241278d5fe853b3de743087be7f5f4c6f7d6d22a3b524d323eecec505"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ad1af54a62ffe39cf34db1aa6ed1a1873bd548f6401db39d8e7cd060b9211f82"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e7048abd75fe40712005bcfc06bb44b9dfcd8e101dda2ecf2f5aa46115ad07ca"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:160291c60081f23ee43d44b08a7e5fb76681221a8e10b3139618c5a9a291b84e"}, + {file = "propcache-0.2.1-cp311-cp311-win32.whl", hash = "sha256:819ce3b883b7576ca28da3861c7e1a88afd08cc8c96908e08a3f4dd64a228034"}, + {file = "propcache-0.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:edc9fc7051e3350643ad929df55c451899bb9ae6d24998a949d2e4c87fb596d3"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:081a430aa8d5e8876c6909b67bd2d937bfd531b0382d3fdedb82612c618bc41a"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2ccec9ac47cf4e04897619c0e0c1a48c54a71bdf045117d3a26f80d38ab1fb0"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14d86fe14b7e04fa306e0c43cdbeebe6b2c2156a0c9ce56b815faacc193e320d"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:049324ee97bb67285b49632132db351b41e77833678432be52bdd0289c0e05e4"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cd9a1d071158de1cc1c71a26014dcdfa7dd3d5f4f88c298c7f90ad6f27bb46d"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98110aa363f1bb4c073e8dcfaefd3a5cea0f0834c2aab23dda657e4dab2f53b5"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:647894f5ae99c4cf6bb82a1bb3a796f6e06af3caa3d32e26d2350d0e3e3faf24"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfd3223c15bebe26518d58ccf9a39b93948d3dcb3e57a20480dfdd315356baff"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d71264a80f3fcf512eb4f18f59423fe82d6e346ee97b90625f283df56aee103f"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e73091191e4280403bde6c9a52a6999d69cdfde498f1fdf629105247599b57ec"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3935bfa5fede35fb202c4b569bb9c042f337ca4ff7bd540a0aa5e37131659348"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f508b0491767bb1f2b87fdfacaba5f7eddc2f867740ec69ece6d1946d29029a6"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1672137af7c46662a1c2be1e8dc78cb6d224319aaa40271c9257d886be4363a6"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b74c261802d3d2b85c9df2dfb2fa81b6f90deeef63c2db9f0e029a3cac50b518"}, + {file = "propcache-0.2.1-cp312-cp312-win32.whl", hash = "sha256:d09c333d36c1409d56a9d29b3a1b800a42c76a57a5a8907eacdbce3f18768246"}, + {file = "propcache-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:c214999039d4f2a5b2073ac506bba279945233da8c786e490d411dfc30f855c1"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aca405706e0b0a44cc6bfd41fbe89919a6a56999157f6de7e182a990c36e37bc"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:12d1083f001ace206fe34b6bdc2cb94be66d57a850866f0b908972f90996b3e9"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d93f3307ad32a27bda2e88ec81134b823c240aa3abb55821a8da553eed8d9439"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba278acf14471d36316159c94a802933d10b6a1e117b8554fe0d0d9b75c9d536"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4e6281aedfca15301c41f74d7005e6e3f4ca143584ba696ac69df4f02f40d629"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b750a8e5a1262434fb1517ddf64b5de58327f1adc3524a5e44c2ca43305eb0b"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf72af5e0fb40e9babf594308911436c8efde3cb5e75b6f206c34ad18be5c052"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2d0a12018b04f4cb820781ec0dffb5f7c7c1d2a5cd22bff7fb055a2cb19ebce"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e800776a79a5aabdb17dcc2346a7d66d0777e942e4cd251defeb084762ecd17d"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:4160d9283bd382fa6c0c2b5e017acc95bc183570cd70968b9202ad6d8fc48dce"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:30b43e74f1359353341a7adb783c8f1b1c676367b011709f466f42fda2045e95"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:58791550b27d5488b1bb52bc96328456095d96206a250d28d874fafe11b3dfaf"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f022d381747f0dfe27e99d928e31bc51a18b65bb9e481ae0af1380a6725dd1f"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:297878dc9d0a334358f9b608b56d02e72899f3b8499fc6044133f0d319e2ec30"}, + {file = "propcache-0.2.1-cp313-cp313-win32.whl", hash = "sha256:ddfab44e4489bd79bda09d84c430677fc7f0a4939a73d2bba3073036f487a0a6"}, + {file = "propcache-0.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:556fc6c10989f19a179e4321e5d678db8eb2924131e64652a51fe83e4c3db0e1"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6a9a8c34fb7bb609419a211e59da8887eeca40d300b5ea8e56af98f6fbbb1541"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae1aa1cd222c6d205853b3013c69cd04515f9d6ab6de4b0603e2e1c33221303e"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:accb6150ce61c9c4b7738d45550806aa2b71c7668c6942f17b0ac182b6142fd4"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5eee736daafa7af6d0a2dc15cc75e05c64f37fc37bafef2e00d77c14171c2097"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7a31fc1e1bd362874863fdeed71aed92d348f5336fd84f2197ba40c59f061bd"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba4cfa1052819d16699e1d55d18c92b6e094d4517c41dd231a8b9f87b6fa681"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f089118d584e859c62b3da0892b88a83d611c2033ac410e929cb6754eec0ed16"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:781e65134efaf88feb447e8c97a51772aa75e48b794352f94cb7ea717dedda0d"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31f5af773530fd3c658b32b6bdc2d0838543de70eb9a2156c03e410f7b0d3aae"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:a7a078f5d37bee6690959c813977da5291b24286e7b962e62a94cec31aa5188b"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cea7daf9fc7ae6687cf1e2c049752f19f146fdc37c2cc376e7d0032cf4f25347"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:8b3489ff1ed1e8315674d0775dc7d2195fb13ca17b3808721b54dbe9fd020faf"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9403db39be1393618dd80c746cb22ccda168efce239c73af13c3763ef56ffc04"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5d97151bc92d2b2578ff7ce779cdb9174337390a535953cbb9452fb65164c587"}, + {file = "propcache-0.2.1-cp39-cp39-win32.whl", hash = "sha256:9caac6b54914bdf41bcc91e7eb9147d331d29235a7c967c150ef5df6464fd1bb"}, + {file = "propcache-0.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:92fc4500fcb33899b05ba73276dfb684a20d31caa567b7cb5252d48f896a91b1"}, + {file = "propcache-0.2.1-py3-none-any.whl", hash = "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54"}, + {file = "propcache-0.2.1.tar.gz", hash = "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64"}, +] + +[[package]] +name = "protobuf" +version = "5.29.2" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "protobuf-5.29.2-cp310-abi3-win32.whl", hash = "sha256:c12ba8249f5624300cf51c3d0bfe5be71a60c63e4dcf51ffe9a68771d958c851"}, + {file = "protobuf-5.29.2-cp310-abi3-win_amd64.whl", hash = "sha256:842de6d9241134a973aab719ab42b008a18a90f9f07f06ba480df268f86432f9"}, + {file = "protobuf-5.29.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a0c53d78383c851bfa97eb42e3703aefdc96d2036a41482ffd55dc5f529466eb"}, + {file = "protobuf-5.29.2-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:494229ecd8c9009dd71eda5fd57528395d1eacdf307dbece6c12ad0dd09e912e"}, + {file = "protobuf-5.29.2-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:b6b0d416bbbb9d4fbf9d0561dbfc4e324fd522f61f7af0fe0f282ab67b22477e"}, + {file = "protobuf-5.29.2-cp38-cp38-win32.whl", hash = "sha256:e621a98c0201a7c8afe89d9646859859be97cb22b8bf1d8eacfd90d5bda2eb19"}, + {file = "protobuf-5.29.2-cp38-cp38-win_amd64.whl", hash = "sha256:13d6d617a2a9e0e82a88113d7191a1baa1e42c2cc6f5f1398d3b054c8e7e714a"}, + {file = "protobuf-5.29.2-cp39-cp39-win32.whl", hash = "sha256:36000f97ea1e76e8398a3f02936aac2a5d2b111aae9920ec1b769fc4a222c4d9"}, + {file = "protobuf-5.29.2-cp39-cp39-win_amd64.whl", hash = "sha256:2d2e674c58a06311c8e99e74be43e7f3a8d1e2b2fdf845eaa347fbd866f23355"}, + {file = "protobuf-5.29.2-py3-none-any.whl", hash = "sha256:fde4554c0e578a5a0bcc9a276339594848d1e89f9ea47b4427c80e5d72f90181"}, + {file = "protobuf-5.29.2.tar.gz", hash = "sha256:b2cc8e8bb7c9326996f0e160137b0861f1a82162502658df2951209d0cb0309e"}, +] + +[[package]] +name = "pycodestyle" +version = "2.12.1" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3"}, + {file = "pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"}, +] + +[[package]] +name = "pyflakes" +version = "3.2.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, +] + +[[package]] +name = "pytest" +version = "8.2.2" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"}, + {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2.0" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pyyaml" +version = "6.0.2" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, +] + +[[package]] +name = "requests" +version = "2.32.3" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.8" +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "tomli" +version = "2.2.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[[package]] +name = "urllib3" +version = "2.3.0" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.9" +files = [ + {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, + {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "websocket-client" +version = "1.8.0" +description = "WebSocket client for Python with low level API options" +optional = false +python-versions = ">=3.8" +files = [ + {file = "websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526"}, + {file = "websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da"}, +] + +[package.extras] +docs = ["Sphinx (>=6.0)", "myst-parser (>=2.0.0)", "sphinx-rtd-theme (>=1.1.0)"] +optional = ["python-socks", "wsaccel"] +test = ["websockets"] + +[[package]] +name = "yarl" +version = "1.18.3" +description = "Yet another URL library" +optional = false +python-versions = ">=3.9" +files = [ + {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"}, + {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"}, + {file = "yarl-1.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:602d98f2c2d929f8e697ed274fbadc09902c4025c5a9963bf4e9edfc3ab6f7ed"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c654d5207c78e0bd6d749f6dae1dcbbfde3403ad3a4b11f3c5544d9906969dde"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5094d9206c64181d0f6e76ebd8fb2f8fe274950a63890ee9e0ebfd58bf9d787b"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35098b24e0327fc4ebdc8ffe336cee0a87a700c24ffed13161af80124b7dc8e5"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3236da9272872443f81fedc389bace88408f64f89f75d1bdb2256069a8730ccc"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2c08cc9b16f4f4bc522771d96734c7901e7ebef70c6c5c35dd0f10845270bcd"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80316a8bd5109320d38eef8833ccf5f89608c9107d02d2a7f985f98ed6876990"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1e1cc06da1491e6734f0ea1e6294ce00792193c463350626571c287c9a704db"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fea09ca13323376a2fdfb353a5fa2e59f90cd18d7ca4eaa1fd31f0a8b4f91e62"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e3b9fd71836999aad54084906f8663dffcd2a7fb5cdafd6c37713b2e72be1760"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:757e81cae69244257d125ff31663249b3013b5dc0a8520d73694aed497fb195b"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b1771de9944d875f1b98a745bc547e684b863abf8f8287da8466cf470ef52690"}, + {file = "yarl-1.18.3-cp310-cp310-win32.whl", hash = "sha256:8874027a53e3aea659a6d62751800cf6e63314c160fd607489ba5c2edd753cf6"}, + {file = "yarl-1.18.3-cp310-cp310-win_amd64.whl", hash = "sha256:93b2e109287f93db79210f86deb6b9bbb81ac32fc97236b16f7433db7fc437d8"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8503ad47387b8ebd39cbbbdf0bf113e17330ffd339ba1144074da24c545f0069"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02ddb6756f8f4517a2d5e99d8b2f272488e18dd0bfbc802f31c16c6c20f22193"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a283dd2882ac98cc6318384f565bffc751ab564605959df4752d42483ad889"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d980e0325b6eddc81331d3f4551e2a333999fb176fd153e075c6d1c2530aa8a8"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b643562c12680b01e17239be267bc306bbc6aac1f34f6444d1bded0c5ce438ca"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c017a3b6df3a1bd45b9fa49a0f54005e53fbcad16633870104b66fa1a30a29d8"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75674776d96d7b851b6498f17824ba17849d790a44d282929c42dbb77d4f17ae"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccaa3a4b521b780a7e771cc336a2dba389a0861592bbce09a476190bb0c8b4b3"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d06d3005e668744e11ed80812e61efd77d70bb7f03e33c1598c301eea20efbb"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9d41beda9dc97ca9ab0b9888cb71f7539124bc05df02c0cff6e5acc5a19dcc6e"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ba23302c0c61a9999784e73809427c9dbedd79f66a13d84ad1b1943802eaaf59"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6748dbf9bfa5ba1afcc7556b71cda0d7ce5f24768043a02a58846e4a443d808d"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0b0cad37311123211dc91eadcb322ef4d4a66008d3e1bdc404808992260e1a0e"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fb2171a4486bb075316ee754c6d8382ea6eb8b399d4ec62fde2b591f879778a"}, + {file = "yarl-1.18.3-cp311-cp311-win32.whl", hash = "sha256:61b1a825a13bef4a5f10b1885245377d3cd0bf87cba068e1d9a88c2ae36880e1"}, + {file = "yarl-1.18.3-cp311-cp311-win_amd64.whl", hash = "sha256:b9d60031cf568c627d028239693fd718025719c02c9f55df0a53e587aab951b5"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285"}, + {file = "yarl-1.18.3-cp312-cp312-win32.whl", hash = "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2"}, + {file = "yarl-1.18.3-cp312-cp312-win_amd64.whl", hash = "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:90adb47ad432332d4f0bc28f83a5963f426ce9a1a8809f5e584e704b82685dcb"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:913829534200eb0f789d45349e55203a091f45c37a2674678744ae52fae23efa"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef9f7768395923c3039055c14334ba4d926f3baf7b776c923c93d80195624782"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a19f62ff30117e706ebc9090b8ecc79aeb77d0b1f5ec10d2d27a12bc9f66d0"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e17c9361d46a4d5addf777c6dd5eab0715a7684c2f11b88c67ac37edfba6c482"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a74a13a4c857a84a845505fd2d68e54826a2cd01935a96efb1e9d86c728e186"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f7ce59d6ee7741af71d82020346af364949314ed3d87553763a2df1829cc58"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f52a265001d830bc425f82ca9eabda94a64a4d753b07d623a9f2863fde532b53"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:82123d0c954dc58db301f5021a01854a85bf1f3bb7d12ae0c01afc414a882ca2"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2ec9bbba33b2d00999af4631a3397d1fd78290c48e2a3e52d8dd72db3a067ac8"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fbd6748e8ab9b41171bb95c6142faf068f5ef1511935a0aa07025438dd9a9bc1"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:877d209b6aebeb5b16c42cbb377f5f94d9e556626b1bfff66d7b0d115be88d0a"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b464c4ab4bfcb41e3bfd3f1c26600d038376c2de3297760dfe064d2cb7ea8e10"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8d39d351e7faf01483cc7ff7c0213c412e38e5a340238826be7e0e4da450fdc8"}, + {file = "yarl-1.18.3-cp313-cp313-win32.whl", hash = "sha256:61ee62ead9b68b9123ec24bc866cbef297dd266175d53296e2db5e7f797f902d"}, + {file = "yarl-1.18.3-cp313-cp313-win_amd64.whl", hash = "sha256:578e281c393af575879990861823ef19d66e2b1d0098414855dd367e234f5b3c"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:61e5e68cb65ac8f547f6b5ef933f510134a6bf31bb178be428994b0cb46c2a04"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe57328fbc1bfd0bd0514470ac692630f3901c0ee39052ae47acd1d90a436719"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a440a2a624683108a1b454705ecd7afc1c3438a08e890a1513d468671d90a04e"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c7907c8548bcd6ab860e5f513e727c53b4a714f459b084f6580b49fa1b9cee"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4f6450109834af88cb4cc5ecddfc5380ebb9c228695afc11915a0bf82116789"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9ca04806f3be0ac6d558fffc2fdf8fcef767e0489d2684a21912cc4ed0cd1b8"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77a6e85b90a7641d2e07184df5557132a337f136250caafc9ccaa4a2a998ca2c"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6333c5a377c8e2f5fae35e7b8f145c617b02c939d04110c76f29ee3676b5f9a5"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0b3c92fa08759dbf12b3a59579a4096ba9af8dd344d9a813fc7f5070d86bbab1"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4ac515b860c36becb81bb84b667466885096b5fc85596948548b667da3bf9f24"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:045b8482ce9483ada4f3f23b3774f4e1bf4f23a2d5c912ed5170f68efb053318"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:a4bb030cf46a434ec0225bddbebd4b89e6471814ca851abb8696170adb163985"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:54d6921f07555713b9300bee9c50fb46e57e2e639027089b1d795ecd9f7fa910"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1d407181cfa6e70077df3377938c08012d18893f9f20e92f7d2f314a437c30b1"}, + {file = "yarl-1.18.3-cp39-cp39-win32.whl", hash = "sha256:ac36703a585e0929b032fbaab0707b75dc12703766d0b53486eabd5139ebadd5"}, + {file = "yarl-1.18.3-cp39-cp39-win_amd64.whl", hash = "sha256:ba87babd629f8af77f557b61e49e7c7cac36f22f871156b91e10a6e9d4f829e9"}, + {file = "yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b"}, + {file = "yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1"}, +] + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" +propcache = ">=0.2.0" + +[metadata] +lock-version = "2.0" +python-versions = ">=3.9" +content-hash = "36fdc1b45e02eec60320fa7e15dfdac8c8d090b322111d7dee7d5ab3ba9f1b8c" diff --git a/poetry.toml b/poetry.toml new file mode 100644 index 0000000..1164fa7 --- /dev/null +++ b/poetry.toml @@ -0,0 +1,5 @@ +[virtualenvs] +create = true +in-project = true +prefer-active-python = true +prompt = "cf-python-client" diff --git a/pyproject.toml b/pyproject.toml index 44b812c..af468cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,4 +4,43 @@ exclude = ''' ^/( (main\/cloudfoundry_client\/dropsonde.*) )/ -''' \ No newline at end of file +''' + +[tool.poetry] +name = "cloudfoundry_client" +version = "1.37.1" +description = "A client library for CloudFoundry" +authors = ["Benjamin Einaudi "] +readme = "README.rst" +homepage = "https://pypi.org/project/cloudfoundry-client/" +documentation = "https://pypi.org/project/cloudfoundry-client/" +repository = "https://github.com/cloudfoundry-community/cf-python-client" +keywords = ["cloudfoundry", "cf"] +include = ["requirements.txt"] + +[tool.poetry.dependencies] +python = ">=3.9" +aiohttp = ">=3.8.0" +protobuf = ">= 3.20.0, < 6.0.0" +oauth2-client= "1.4.2" +websocket-client= "~1.8.0" +PyYAML = ">=6.0" +requests = ">=2.5.0" +polling2= "0.5.0" + +[tool.poetry.group.dev.dependencies] +black= "24.4.2" +flake8= "7.1.0" +pytest = "~8.2.2" + +[tool.poetry.scripts] +cloudfoundry-client = "cloudfoundry_client.main.main:main" + +[tool.pytest.ini_options] +console_output_style = "count" +pythonpath = [".", "tests",] +testpaths = ["tests"] + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/requirements.txt b/requirements.txt index 86e8e27..003888d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,3 @@ oauth2-client==1.4.2 websocket-client~=1.8.0 PyYAML>=6.0 requests>=2.5.0 -polling2==0.5.0 From d9ad96cf943e486715fed7765ffa0d67ddab5c3e Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sat, 4 Jan 2025 15:30:16 +0100 Subject: [PATCH 183/264] chore(dependencies) upgrade dependencies and remove delivered dependencies file --- poetry.lock | 58 +++++++++++++++--------------- pyproject.toml | 5 ++- requirements.txt | 6 ---- setup.py | 94 ------------------------------------------------ 4 files changed, 31 insertions(+), 132 deletions(-) delete mode 100644 requirements.txt delete mode 100644 setup.py diff --git a/poetry.lock b/poetry.lock index c35a13c..6ff95e0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -155,33 +155,33 @@ tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] [[package]] name = "black" -version = "24.4.2" +version = "24.10.0" description = "The uncompromising code formatter." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "black-24.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce"}, - {file = "black-24.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021"}, - {file = "black-24.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063"}, - {file = "black-24.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96"}, - {file = "black-24.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474"}, - {file = "black-24.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c"}, - {file = "black-24.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb"}, - {file = "black-24.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1"}, - {file = "black-24.4.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d"}, - {file = "black-24.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04"}, - {file = "black-24.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc"}, - {file = "black-24.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0"}, - {file = "black-24.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7"}, - {file = "black-24.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94"}, - {file = "black-24.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8"}, - {file = "black-24.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c"}, - {file = "black-24.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1"}, - {file = "black-24.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741"}, - {file = "black-24.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e"}, - {file = "black-24.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7"}, - {file = "black-24.4.2-py3-none-any.whl", hash = "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c"}, - {file = "black-24.4.2.tar.gz", hash = "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d"}, + {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, + {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, + {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, + {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, + {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, + {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, + {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, + {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, + {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, + {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, + {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, + {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, + {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, + {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, + {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, + {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, + {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, + {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, + {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, + {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, + {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, + {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, ] [package.dependencies] @@ -195,7 +195,7 @@ typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} [package.extras] colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +d = ["aiohttp (>=3.10)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] @@ -352,13 +352,13 @@ test = ["pytest (>=6)"] [[package]] name = "flake8" -version = "7.1.0" +version = "7.1.1" description = "the modular source code checker: pep8 pyflakes and co" optional = false python-versions = ">=3.8.1" files = [ - {file = "flake8-7.1.0-py2.py3-none-any.whl", hash = "sha256:2e416edcc62471a64cea09353f4e7bdba32aeb079b6e360554c659a122b1bc6a"}, - {file = "flake8-7.1.0.tar.gz", hash = "sha256:48a07b626b55236e0fb4784ee69a465fbf59d79eec1f5b4785c3d3bc57d17aa5"}, + {file = "flake8-7.1.1-py2.py3-none-any.whl", hash = "sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213"}, + {file = "flake8-7.1.1.tar.gz", hash = "sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38"}, ] [package.dependencies] @@ -1118,4 +1118,4 @@ propcache = ">=0.2.0" [metadata] lock-version = "2.0" python-versions = ">=3.9" -content-hash = "36fdc1b45e02eec60320fa7e15dfdac8c8d090b322111d7dee7d5ab3ba9f1b8c" +content-hash = "1a7ece3e142ed461135a07c5000dd043a1ea9a517e7199c3c93e0788d481119b" diff --git a/pyproject.toml b/pyproject.toml index af468cc..be25d17 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,6 @@ homepage = "https://pypi.org/project/cloudfoundry-client/" documentation = "https://pypi.org/project/cloudfoundry-client/" repository = "https://github.com/cloudfoundry-community/cf-python-client" keywords = ["cloudfoundry", "cf"] -include = ["requirements.txt"] [tool.poetry.dependencies] python = ">=3.9" @@ -29,8 +28,8 @@ requests = ">=2.5.0" polling2= "0.5.0" [tool.poetry.group.dev.dependencies] -black= "24.4.2" -flake8= "7.1.0" +black= "24.10.0" +flake8= "7.1.1" pytest = "~8.2.2" [tool.poetry.scripts] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 003888d..0000000 --- a/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -aiohttp>=3.8.0 -protobuf>= 3.20.0, < 6.0.0 -oauth2-client==1.4.2 -websocket-client~=1.8.0 -PyYAML>=6.0 -requests>=2.5.0 diff --git a/setup.py b/setup.py deleted file mode 100644 index ddcfa0a..0000000 --- a/setup.py +++ /dev/null @@ -1,94 +0,0 @@ -import os -import shutil -import subprocess -import sys - -from setuptools import setup, find_packages, Command, __version__ - -src_dir = "main" -package_directory = "cloudfoundry_client" -package_name = "cloudfoundry-client" -dropsonde_src = "vendors/dropsonde-protocol" -dropsonde_dest = "dropsonde" -sys.path.insert(0, os.path.realpath(src_dir)) - -version_file = "%s/%s/__init__.py" % (src_dir, package_directory) -with open(version_file, "r") as f: - for line in f.readlines(): - if line.find("__version__") >= 0: - exec(line) - break - else: - raise AssertionError("Failed to load version from %s" % version_file) - - -def purge_sub_dir(path): - shutil.rmtree(os.path.join(os.path.dirname(__file__), path)) - - -if "test" in sys.argv[1:]: - print("%s added" % os.path.join(os.getcwd(), "test")) - sys.path.append(os.path.join(os.getcwd(), "test")) - - -class GenerateCommand(Command): - description = "generate protobuf class generation" - user_options = [] - - def initialize_options(self): - pass - - def finalize_options(self): - pass - - def run(self): - source_path = os.path.join(os.path.dirname(__file__), dropsonde_src) - for file_protobuf in os.listdir(source_path): - if file_protobuf.endswith(".proto"): - file_path = os.path.join(source_path, file_protobuf) - dest_path = os.path.join(src_dir, package_directory, dropsonde_dest) - print("Generating %s from %s to %s" % (file_protobuf, file_path, dest_path)) - subprocess.call( - [ - "protoc", - "--proto_path", - source_path, - "--python_out=%s" % dest_path, - file_path, - ] - ) - - -setup( - name=package_name, - version=__version__, - zip_safe=True, - packages=find_packages(where=src_dir), - author="Benjamin Einaudi", - author_email="antechrestos@gmail.com", - description="A client library for CloudFoundry", - long_description=open("README.rst").read(), - url="http://github.com/antechrestos/cf-python-client", - python_requires=">=3.8", - classifiers=[ - "Programming Language :: Python", - "Natural Language :: English", - "Operating System :: OS Independent", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Topic :: Communications", - ], - entry_points={ - "console_scripts": [ - "cloudfoundry-client = %s.main.main:main" % package_directory, - ] - }, - cmdclass=dict(generate=GenerateCommand), - package_dir={package_directory: "%s/%s" % (src_dir, package_directory)}, - install_requires=[requirement.rstrip(" \r\n") for requirement in open("requirements.txt")], - test_suite="test", -) From 0fc3f7a945cc6e9ec72d77f4c16a9b8e1beaa5a0 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Sat, 4 Jan 2025 00:02:45 +0100 Subject: [PATCH 184/264] fixes(api) allow no v2 api Allow client without no V2 api Closes #220 --- cloudfoundry_client/client.py | 62 ++++++++++++++----- cloudfoundry_client/common_objects.py | 4 +- cloudfoundry_client/v2/apps.py | 2 +- cloudfoundry_client/v2/buildpacks.py | 2 +- cloudfoundry_client/v2/entities.py | 4 +- cloudfoundry_client/v2/events.py | 2 +- cloudfoundry_client/v2/jobs.py | 2 +- cloudfoundry_client/v2/resources.py | 2 +- cloudfoundry_client/v2/routes.py | 2 +- cloudfoundry_client/v2/service_bindings.py | 2 +- cloudfoundry_client/v2/service_brokers.py | 2 +- cloudfoundry_client/v2/service_instances.py | 2 +- cloudfoundry_client/v2/service_keys.py | 2 +- .../v2/service_plan_visibilities.py | 2 +- cloudfoundry_client/v2/service_plans.py | 2 +- cloudfoundry_client/v2/spaces.py | 2 +- cloudfoundry_client/v3/apps.py | 2 +- cloudfoundry_client/v3/buildpacks.py | 2 +- cloudfoundry_client/v3/domains.py | 4 +- cloudfoundry_client/v3/entities.py | 2 +- cloudfoundry_client/v3/feature_flags.py | 2 +- cloudfoundry_client/v3/isolation_segments.py | 2 +- cloudfoundry_client/v3/jobs.py | 2 +- cloudfoundry_client/v3/organization_quotas.py | 2 +- cloudfoundry_client/v3/organizations.py | 2 +- cloudfoundry_client/v3/processes.py | 2 +- cloudfoundry_client/v3/roles.py | 2 +- cloudfoundry_client/v3/security_groups.py | 2 +- cloudfoundry_client/v3/service_brokers.py | 2 +- .../v3/service_credential_bindings.py | 2 +- cloudfoundry_client/v3/service_instances.py | 2 +- cloudfoundry_client/v3/service_offerings.py | 2 +- cloudfoundry_client/v3/service_plans.py | 2 +- cloudfoundry_client/v3/spaces.py | 2 +- cloudfoundry_client/v3/tasks.py | 6 +- tests/abstract_test_case.py | 55 ++++++++-------- tests/test_client.py | 25 +++++++- 37 files changed, 139 insertions(+), 81 deletions(-) diff --git a/cloudfoundry_client/client.py b/cloudfoundry_client/client.py index 77cc556..7794741 100644 --- a/cloudfoundry_client/client.py +++ b/cloudfoundry_client/client.py @@ -52,18 +52,28 @@ class Info: def __init__( self, - api_v2_version: str, + api_v2_url: str, + api_v3_url: str, authorization_endpoint: str, api_endpoint: str, doppler_endpoint: Optional[str], log_stream_endpoint: Optional[str], ): - self.api_v2_version = api_v2_version + self._api_v2_url = api_v2_url + self._api_v3_url = api_v3_url self.authorization_endpoint = authorization_endpoint self.api_endpoint = api_endpoint self.doppler_endpoint = doppler_endpoint self.log_stream_endpoint = log_stream_endpoint + @property + def api_v2_url(self) -> Optional[str]: + return self._api_v2_url + + @property + def api_v3_url(self) -> Optional[str]: + return self._api_v3_url + class NetworkingV1External(object): def __init__(self, target_endpoint: str, credential_manager: "CloudFoundryClient"): @@ -83,18 +93,18 @@ def __init__(self, target_endpoint: str, credential_manager: "CloudFoundryClient self.service_plans = ServicePlanManagerV2(target_endpoint, credential_manager) # Default implementations self.event = EventManager(target_endpoint, credential_manager) - self.organizations = EntityManagerV2(target_endpoint, credential_manager, "/v2/organizations") - self.private_domains = EntityManagerV2(target_endpoint, credential_manager, "/v2/private_domains") + self.organizations = EntityManagerV2(target_endpoint, credential_manager, "/organizations") + self.private_domains = EntityManagerV2(target_endpoint, credential_manager, "/private_domains") self.routes = RouteManager(target_endpoint, credential_manager) - self.services = EntityManagerV2(target_endpoint, credential_manager, "/v2/services") - self.shared_domains = EntityManagerV2(target_endpoint, credential_manager, "/v2/shared_domains") + self.services = EntityManagerV2(target_endpoint, credential_manager, "/services") + self.shared_domains = EntityManagerV2(target_endpoint, credential_manager, "/shared_domains") self.spaces = SpaceManagerV2(target_endpoint, credential_manager) - self.stacks = EntityManagerV2(target_endpoint, credential_manager, "/v2/stacks") + self.stacks = EntityManagerV2(target_endpoint, credential_manager, "/stacks") self.user_provided_service_instances = EntityManagerV2( - target_endpoint, credential_manager, "/v2/user_provided_service_instances" + target_endpoint, credential_manager, "/user_provided_service_instances" ) - self.security_groups = EntityManagerV2(target_endpoint, credential_manager, "/v2/security_groups") - self.users = EntityManagerV2(target_endpoint, credential_manager, "/v2/users") + self.security_groups = EntityManagerV2(target_endpoint, credential_manager, "/security_groups") + self.users = EntityManagerV2(target_endpoint, credential_manager, "/users") # Resources implementation used by push operation self.resources = ResourceManager(target_endpoint, credential_manager) @@ -146,8 +156,6 @@ def __init__(self, target_endpoint: str, client_id: str = "cf", client_secret: s self.login_hint = kwargs.get("login_hint") target_endpoint_trimmed = target_endpoint.rstrip("/") info = self._get_info(target_endpoint_trimmed, proxy, verify=verify) - if not info.api_v2_version.startswith("2."): - raise AssertionError("Only version 2 is supported for now. Found %s" % info.api_v2_version) service_information = ServiceInformation( None, "%s/oauth/token" % info.authorization_endpoint, client_id, client_secret, [], verify ) @@ -156,8 +164,16 @@ def __init__(self, target_endpoint: str, client_id: str = "cf", client_secret: s proxies=proxy, user_agent=kwargs.get("user_agent", "cf-python-client") ) - self.v2 = V2(target_endpoint_trimmed, self) - self.v3 = V3(target_endpoint_trimmed, self) + self._v2 = ( + V2(info.api_v2_url, self) + if info.api_v2_url is not None + else None + ) + self._v3 = ( + V3(info.api_v3_url, self) + if info.api_v3_url is not None + else None + ) self._doppler = ( DopplerClient( info.doppler_endpoint, @@ -181,6 +197,18 @@ def __init__(self, target_endpoint: str, client_id: str = "cf", client_secret: s self.networking_v1_external = NetworkingV1External(target_endpoint_trimmed, self) self.info = info + @property + def v2(self) -> V2: + if self._v2 is None: + raise NotImplementedError("No V2 endpoint for this instance") + return self._v2 + + @property + def v3(self) -> V3: + if self._v3 is None: + raise NotImplementedError("No V3 endpoint for this instance") + return self._v3 + @property def doppler(self) -> DopplerClient: if self._doppler is None: @@ -194,7 +222,6 @@ def rlpgateway(self): if self._rlpgateway is None: raise NotImplementedError("No RLP gateway endpoint for this instance") else: - return self._rlpgateway def _get_info(self, target_endpoint: str, proxy: Optional[dict] = None, verify: bool = True) -> Info: @@ -206,8 +233,11 @@ def _get_info(self, target_endpoint: str, proxy: Optional[dict] = None, verify: root_links = root_info["links"] logging = root_links.get("logging") log_stream = root_links.get("log_stream") + cloud_controller_v2 = root_links.get("cloud_controller_v2") + cloud_controller_v3 = root_links.get("cloud_controller_v3") return Info( - root_links["cloud_controller_v2"]["meta"]["version"], + cloud_controller_v2["href"] if cloud_controller_v2 is not None else None, + cloud_controller_v3["href"] if cloud_controller_v3 is not None else None, self._resolve_login_endpoint(root_links), target_endpoint, logging.get("href") if logging is not None else None, diff --git a/cloudfoundry_client/common_objects.py b/cloudfoundry_client/common_objects.py index 9c3aa0f..36ef6ea 100644 --- a/cloudfoundry_client/common_objects.py +++ b/cloudfoundry_client/common_objects.py @@ -1,5 +1,5 @@ import json -from typing import Callable, TypeVar, Generic, List, Union +from typing import Callable, TypeVar, Generic, List, Optional class Request(dict): @@ -21,7 +21,7 @@ def __init__(self, *args, **kwargs): class Pagination(Generic[ENTITY]): def __init__(self, first_page: JsonObject, total_result: int, - next_page_loader: Callable[[JsonObject], Union[None, JsonObject]], + next_page_loader: Callable[[JsonObject], Optional[JsonObject]], resources_accessor: Callable[[JsonObject], List[JsonObject]], instance_creator: Callable[[JsonObject], ENTITY]): self._first_page = first_page diff --git a/cloudfoundry_client/v2/apps.py b/cloudfoundry_client/v2/apps.py index cf68ed8..d060bff 100644 --- a/cloudfoundry_client/v2/apps.py +++ b/cloudfoundry_client/v2/apps.py @@ -77,7 +77,7 @@ class AppManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(AppManager, self).__init__( - target_endpoint, client, "/v2/apps", lambda pairs: Application(target_endpoint, client, pairs) + target_endpoint, client, "/apps", lambda pairs: Application(target_endpoint, client, pairs) ) def get_stats(self, application_guid: str) -> Dict[str, JsonObject]: diff --git a/cloudfoundry_client/v2/buildpacks.py b/cloudfoundry_client/v2/buildpacks.py index 958a283..8e1276b 100644 --- a/cloudfoundry_client/v2/buildpacks.py +++ b/cloudfoundry_client/v2/buildpacks.py @@ -8,7 +8,7 @@ class BuildpackManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(BuildpackManager, self).__init__(target_endpoint, client, "/v2/buildpacks") + super(BuildpackManager, self).__init__(target_endpoint, client, "/buildpacks") def update(self, buildpack_guid: str, parameters: dict) -> Entity: return super(BuildpackManager, self)._update(buildpack_guid, parameters) diff --git a/cloudfoundry_client/v2/entities.py b/cloudfoundry_client/v2/entities.py index d9ace97..e324d9e 100644 --- a/cloudfoundry_client/v2/entities.py +++ b/cloudfoundry_client/v2/entities.py @@ -1,5 +1,5 @@ from functools import partial, reduce -from typing import Callable, List, Tuple, Any, Optional, TYPE_CHECKING, Union +from typing import Callable, List, Tuple, Any, Optional, TYPE_CHECKING from urllib.parse import quote from requests import Response @@ -68,7 +68,7 @@ def _list(self, requested_path: str, entity_builder: Optional[EntityBuilder] = N lambda page: page["resources"], lambda json_object: current_builder(list(json_object.items()))) - def _next_page(self, current_page: JsonObject) -> Union[None, JsonObject]: + def _next_page(self, current_page: JsonObject) -> Optional[JsonObject]: next_url = current_page.get("next_url") if next_url is None: return None diff --git a/cloudfoundry_client/v2/events.py b/cloudfoundry_client/v2/events.py index 9822165..cdec234 100644 --- a/cloudfoundry_client/v2/events.py +++ b/cloudfoundry_client/v2/events.py @@ -8,7 +8,7 @@ class EventManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(EventManager, self).__init__(target_endpoint, client, "/v2/events") + super(EventManager, self).__init__(target_endpoint, client, "/events") def list_by_type(self, event_type: str) -> Generator[Entity, None, None]: return self._list(self.entity_uri, type=event_type) diff --git a/cloudfoundry_client/v2/jobs.py b/cloudfoundry_client/v2/jobs.py index 68bdaed..f1a6f53 100644 --- a/cloudfoundry_client/v2/jobs.py +++ b/cloudfoundry_client/v2/jobs.py @@ -12,4 +12,4 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): self.client = client def get(self, job_guid: str) -> JsonObject: - return self.client.get("%s/v2/jobs/%s" % (self.target_endpoint, job_guid)).json(object_pairs_hook=JsonObject) + return self.client.get("%s/jobs/%s" % (self.target_endpoint, job_guid)).json(object_pairs_hook=JsonObject) diff --git a/cloudfoundry_client/v2/resources.py b/cloudfoundry_client/v2/resources.py index d962daa..2b0dae3 100644 --- a/cloudfoundry_client/v2/resources.py +++ b/cloudfoundry_client/v2/resources.py @@ -12,5 +12,5 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): self.client = client def match(self, items: List[dict]) -> List[JsonObject]: - response = self.client.put("%s/v2/resource_match" % self.client.info.api_endpoint, json=items) + response = self.client.put("%s/resource_match" % self.client.info.api_endpoint, json=items) return response.json(object_pairs_hook=JsonObject) diff --git a/cloudfoundry_client/v2/routes.py b/cloudfoundry_client/v2/routes.py index 1e4d52c..bf9d425 100644 --- a/cloudfoundry_client/v2/routes.py +++ b/cloudfoundry_client/v2/routes.py @@ -8,7 +8,7 @@ class RouteManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(RouteManager, self).__init__(target_endpoint, client, "/v2/routes") + super(RouteManager, self).__init__(target_endpoint, client, "/routes") def create_tcp_route(self, domain_guid: str, space_guid: str, port: Optional[int] = None) -> Entity: request = self._request(domain_guid=domain_guid, space_guid=space_guid) diff --git a/cloudfoundry_client/v2/service_bindings.py b/cloudfoundry_client/v2/service_bindings.py index 00fc625..95f9f5b 100644 --- a/cloudfoundry_client/v2/service_bindings.py +++ b/cloudfoundry_client/v2/service_bindings.py @@ -8,7 +8,7 @@ class ServiceBindingManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceBindingManager, self).__init__(target_endpoint, client, "/v2/service_bindings") + super(ServiceBindingManager, self).__init__(target_endpoint, client, "/service_bindings") def create(self, app_guid: str, instance_guid: str, parameters: Optional[dict] = None, name: Optional[str] = None) -> Entity: request = self._request(app_guid=app_guid, service_instance_guid=instance_guid) diff --git a/cloudfoundry_client/v2/service_brokers.py b/cloudfoundry_client/v2/service_brokers.py index abe44c7..afc2c1a 100644 --- a/cloudfoundry_client/v2/service_brokers.py +++ b/cloudfoundry_client/v2/service_brokers.py @@ -8,7 +8,7 @@ class ServiceBrokerManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceBrokerManager, self).__init__(target_endpoint, client, "/v2/service_brokers") + super(ServiceBrokerManager, self).__init__(target_endpoint, client, "/service_brokers") def create( self, broker_url: str, broker_name: str, auth_username: str, auth_password: str, space_guid: Optional[str] = None diff --git a/cloudfoundry_client/v2/service_instances.py b/cloudfoundry_client/v2/service_instances.py index f17fa7f..edeac74 100644 --- a/cloudfoundry_client/v2/service_instances.py +++ b/cloudfoundry_client/v2/service_instances.py @@ -10,7 +10,7 @@ class ServiceInstanceManager(EntityManager): list_query_parameters = ["page", "results-per-page", "order-direction", "return_user_provided_service_instances"] def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceInstanceManager, self).__init__(target_endpoint, client, "/v2/service_instances") + super(ServiceInstanceManager, self).__init__(target_endpoint, client, "/service_instances") def create( self, diff --git a/cloudfoundry_client/v2/service_keys.py b/cloudfoundry_client/v2/service_keys.py index cb87c3e..4f4da97 100644 --- a/cloudfoundry_client/v2/service_keys.py +++ b/cloudfoundry_client/v2/service_keys.py @@ -8,7 +8,7 @@ class ServiceKeyManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceKeyManager, self).__init__(target_endpoint, client, "/v2/service_keys") + super(ServiceKeyManager, self).__init__(target_endpoint, client, "/service_keys") def create(self, service_instance_guid: str, name: str, parameters: Optional[dict] = None) -> Entity: request = self._request(service_instance_guid=service_instance_guid, name=name) diff --git a/cloudfoundry_client/v2/service_plan_visibilities.py b/cloudfoundry_client/v2/service_plan_visibilities.py index 4d7d513..47bcedf 100644 --- a/cloudfoundry_client/v2/service_plan_visibilities.py +++ b/cloudfoundry_client/v2/service_plan_visibilities.py @@ -8,7 +8,7 @@ class ServicePlanVisibilityManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServicePlanVisibilityManager, self).__init__(target_endpoint, client, "/v2/service_plan_visibilities") + super(ServicePlanVisibilityManager, self).__init__(target_endpoint, client, "/service_plan_visibilities") def create(self, service_plan_guid: str, organization_guid: str) -> Entity: request = self._request() diff --git a/cloudfoundry_client/v2/service_plans.py b/cloudfoundry_client/v2/service_plans.py index 9a94aa8..8c8e766 100644 --- a/cloudfoundry_client/v2/service_plans.py +++ b/cloudfoundry_client/v2/service_plans.py @@ -9,7 +9,7 @@ class ServicePlanManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServicePlanManager, self).__init__(target_endpoint, client, "/v2/service_plans") + super(ServicePlanManager, self).__init__(target_endpoint, client, "/service_plans") def create_from_resource_file(self, path: str) -> Entity: raise NotImplementedError("No creation allowed") diff --git a/cloudfoundry_client/v2/spaces.py b/cloudfoundry_client/v2/spaces.py index 6f6254a..b00743c 100644 --- a/cloudfoundry_client/v2/spaces.py +++ b/cloudfoundry_client/v2/spaces.py @@ -8,7 +8,7 @@ class SpaceManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(SpaceManager, self).__init__(target_endpoint, client, "/v2/spaces") + super(SpaceManager, self).__init__(target_endpoint, client, "/spaces") def delete_unmapped_routes(self, space_guid: str): url = "%s%s/%s/unmapped_routes" % (self.target_endpoint, self.entity_uri, space_guid) diff --git a/cloudfoundry_client/v3/apps.py b/cloudfoundry_client/v3/apps.py index 1453575..338c86c 100644 --- a/cloudfoundry_client/v3/apps.py +++ b/cloudfoundry_client/v3/apps.py @@ -9,7 +9,7 @@ class AppManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(AppManager, self).__init__(target_endpoint, client, "/v3/apps") + super(AppManager, self).__init__(target_endpoint, client, "/apps") def restart(self, application_guid: str): return super(AppManager, self)._post("%s%s/%s/actions/restart" % (self.target_endpoint, diff --git a/cloudfoundry_client/v3/buildpacks.py b/cloudfoundry_client/v3/buildpacks.py index f6a1284..f8930b2 100644 --- a/cloudfoundry_client/v3/buildpacks.py +++ b/cloudfoundry_client/v3/buildpacks.py @@ -8,7 +8,7 @@ class BuildpackManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(BuildpackManager, self).__init__(target_endpoint, client, "/v3/buildpacks") + super(BuildpackManager, self).__init__(target_endpoint, client, "/buildpacks") def create( self, diff --git a/cloudfoundry_client/v3/domains.py b/cloudfoundry_client/v3/domains.py index d1f5db3..0077115 100644 --- a/cloudfoundry_client/v3/domains.py +++ b/cloudfoundry_client/v3/domains.py @@ -21,7 +21,7 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient", **kwargs) class DomainManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(DomainManager, self).__init__(target_endpoint, client, "/v3/domains", Domain) + super(DomainManager, self).__init__(target_endpoint, client, "/domains", Domain) def create( self, @@ -44,7 +44,7 @@ def create( return super(DomainManager, self)._create(data) def list_domains_for_org(self, org_guid: str, **kwargs) -> Pagination[Entity]: - uri = "/v3/organizations/{guid}/domains".format(guid=org_guid) + uri = "/organizations/{guid}/domains".format(guid=org_guid) return self._list(uri, **kwargs) def update(self, domain_guid: str, meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Domain: diff --git a/cloudfoundry_client/v3/entities.py b/cloudfoundry_client/v3/entities.py index 36ec5f4..343a8f1 100644 --- a/cloudfoundry_client/v3/entities.py +++ b/cloudfoundry_client/v3/entities.py @@ -188,7 +188,7 @@ def _entity(json_object: JsonObject) -> Entity: lambda p: p["resources"], _entity) - def _next_page(self, current_page: JsonObject) -> Union[None, JsonObject]: + def _next_page(self, current_page: JsonObject) -> Optional[JsonObject]: pagination = current_page.get("pagination") if ( pagination is None diff --git a/cloudfoundry_client/v3/feature_flags.py b/cloudfoundry_client/v3/feature_flags.py index 47c35d8..3c280ad 100644 --- a/cloudfoundry_client/v3/feature_flags.py +++ b/cloudfoundry_client/v3/feature_flags.py @@ -8,7 +8,7 @@ class FeatureFlagManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(FeatureFlagManager, self).__init__(target_endpoint, client, "/v3/feature_flags") + super(FeatureFlagManager, self).__init__(target_endpoint, client, "/feature_flags") def update(self, name: str, enabled: Optional[bool] = True, custom_error_message: Optional[str] = None) -> Entity: data = {"enabled": enabled, "custom_error_message": custom_error_message} diff --git a/cloudfoundry_client/v3/isolation_segments.py b/cloudfoundry_client/v3/isolation_segments.py index 67fa114..391e4d5 100644 --- a/cloudfoundry_client/v3/isolation_segments.py +++ b/cloudfoundry_client/v3/isolation_segments.py @@ -8,7 +8,7 @@ class IsolationSegmentManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(IsolationSegmentManager, self).__init__(target_endpoint, client, "/v3/isolation_segments") + super(IsolationSegmentManager, self).__init__(target_endpoint, client, "/isolation_segments") def create(self, name: str, meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Entity: data = {"name": name, "metadata": {"labels": meta_labels, "annotations": meta_annotations}} diff --git a/cloudfoundry_client/v3/jobs.py b/cloudfoundry_client/v3/jobs.py index 80bd510..5fd10fb 100644 --- a/cloudfoundry_client/v3/jobs.py +++ b/cloudfoundry_client/v3/jobs.py @@ -15,7 +15,7 @@ class JobTimeout(Exception): class JobManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(JobManager, self).__init__(target_endpoint, client, "/v3/jobs") + super(JobManager, self).__init__(target_endpoint, client, "/jobs") def wait_for_job_completion( self, diff --git a/cloudfoundry_client/v3/organization_quotas.py b/cloudfoundry_client/v3/organization_quotas.py index abe5015..2779f9c 100644 --- a/cloudfoundry_client/v3/organization_quotas.py +++ b/cloudfoundry_client/v3/organization_quotas.py @@ -35,7 +35,7 @@ class DomainsQuota: class OrganizationQuotaManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super().__init__(target_endpoint, client, "/v3/organization_quotas") + super().__init__(target_endpoint, client, "/organization_quotas") def remove(self, guid: str, asynchronous: bool = True) -> Optional[str]: return super()._remove(guid, asynchronous) diff --git a/cloudfoundry_client/v3/organizations.py b/cloudfoundry_client/v3/organizations.py index 2086d31..e7b4639 100644 --- a/cloudfoundry_client/v3/organizations.py +++ b/cloudfoundry_client/v3/organizations.py @@ -8,7 +8,7 @@ class OrganizationManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(OrganizationManager, self).__init__(target_endpoint, client, "/v3/organizations") + super(OrganizationManager, self).__init__(target_endpoint, client, "/organizations") def create( self, name: str, suspended: bool, meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None diff --git a/cloudfoundry_client/v3/processes.py b/cloudfoundry_client/v3/processes.py index cb632f3..f7ee291 100644 --- a/cloudfoundry_client/v3/processes.py +++ b/cloudfoundry_client/v3/processes.py @@ -8,4 +8,4 @@ class ProcessManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ProcessManager, self).__init__(target_endpoint, client, "/v3/processes") + super(ProcessManager, self).__init__(target_endpoint, client, "/processes") diff --git a/cloudfoundry_client/v3/roles.py b/cloudfoundry_client/v3/roles.py index 9123d5a..ed466b9 100644 --- a/cloudfoundry_client/v3/roles.py +++ b/cloudfoundry_client/v3/roles.py @@ -8,7 +8,7 @@ class RoleManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(RoleManager, self).__init__(target_endpoint, client, "/v3/roles") + super(RoleManager, self).__init__(target_endpoint, client, "/roles") def remove(self, role_guid: str, asynchronous: bool = True) -> Optional[str]: return super(RoleManager, self)._remove(role_guid, asynchronous) diff --git a/cloudfoundry_client/v3/security_groups.py b/cloudfoundry_client/v3/security_groups.py index b134e03..fdde7a2 100644 --- a/cloudfoundry_client/v3/security_groups.py +++ b/cloudfoundry_client/v3/security_groups.py @@ -37,7 +37,7 @@ class GloballyEnabled: class SecurityGroupManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(SecurityGroupManager, self).__init__(target_endpoint, client, "/v3/security_groups") + super(SecurityGroupManager, self).__init__(target_endpoint, client, "/security_groups") def create(self, name: str, diff --git a/cloudfoundry_client/v3/service_brokers.py b/cloudfoundry_client/v3/service_brokers.py index c39795b..a354de9 100644 --- a/cloudfoundry_client/v3/service_brokers.py +++ b/cloudfoundry_client/v3/service_brokers.py @@ -8,7 +8,7 @@ class ServiceBrokerManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceBrokerManager, self).__init__(target_endpoint, client, "/v3/service_brokers") + super(ServiceBrokerManager, self).__init__(target_endpoint, client, "/service_brokers") def create( self, diff --git a/cloudfoundry_client/v3/service_credential_bindings.py b/cloudfoundry_client/v3/service_credential_bindings.py index d587b7a..6599686 100644 --- a/cloudfoundry_client/v3/service_credential_bindings.py +++ b/cloudfoundry_client/v3/service_credential_bindings.py @@ -9,7 +9,7 @@ class ServiceCredentialBindingManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(ServiceCredentialBindingManager, self).__init__(target_endpoint, client, - "/v3/service_credential_bindings") + "/service_credential_bindings") def create( self, diff --git a/cloudfoundry_client/v3/service_instances.py b/cloudfoundry_client/v3/service_instances.py index ad33614..695b0ef 100644 --- a/cloudfoundry_client/v3/service_instances.py +++ b/cloudfoundry_client/v3/service_instances.py @@ -9,7 +9,7 @@ class ServiceInstanceManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceInstanceManager, self).__init__(target_endpoint, client, "/v3/service_instances") + super(ServiceInstanceManager, self).__init__(target_endpoint, client, "/service_instances") def create( self, diff --git a/cloudfoundry_client/v3/service_offerings.py b/cloudfoundry_client/v3/service_offerings.py index 37f7d0c..c784ca2 100644 --- a/cloudfoundry_client/v3/service_offerings.py +++ b/cloudfoundry_client/v3/service_offerings.py @@ -8,7 +8,7 @@ class ServiceOfferingsManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceOfferingsManager, self).__init__(target_endpoint, client, "/v3/service_offerings") + super(ServiceOfferingsManager, self).__init__(target_endpoint, client, "/service_offerings") def update(self, guid: str, meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Entity: payload = dict() diff --git a/cloudfoundry_client/v3/service_plans.py b/cloudfoundry_client/v3/service_plans.py index 257c3bf..9e27e15 100644 --- a/cloudfoundry_client/v3/service_plans.py +++ b/cloudfoundry_client/v3/service_plans.py @@ -8,7 +8,7 @@ class ServicePlanManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServicePlanManager, self).__init__(target_endpoint, client, "/v3/service_plans") + super(ServicePlanManager, self).__init__(target_endpoint, client, "/service_plans") def update( self, diff --git a/cloudfoundry_client/v3/spaces.py b/cloudfoundry_client/v3/spaces.py index f871a43..acb3597 100644 --- a/cloudfoundry_client/v3/spaces.py +++ b/cloudfoundry_client/v3/spaces.py @@ -8,7 +8,7 @@ class SpaceManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(SpaceManager, self).__init__(target_endpoint, client, "/v3/spaces") + super(SpaceManager, self).__init__(target_endpoint, client, "/spaces") def create(self, name: str, org_guid: str) -> Entity: return super(SpaceManager, self)._create(dict(name=name, relationships=dict(organization=ToOneRelationship(org_guid)))) diff --git a/cloudfoundry_client/v3/tasks.py b/cloudfoundry_client/v3/tasks.py index a7ce16e..1367348 100644 --- a/cloudfoundry_client/v3/tasks.py +++ b/cloudfoundry_client/v3/tasks.py @@ -8,7 +8,7 @@ class TaskManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(TaskManager, self).__init__(target_endpoint, client, "/v3/tasks") + super(TaskManager, self).__init__(target_endpoint, client, "/tasks") def create( self, @@ -24,7 +24,7 @@ def create( request["disk_in_mb"] = disk_in_mb request["memory_in_mb"] = memory_in_mb request["droplet_guid"] = droplet_guid - return self._post("%s/v3/apps/%s/tasks" % (self.target_endpoint, application_guid), data=request) + return self._post("%s/apps/%s/tasks" % (self.target_endpoint, application_guid), data=request) def cancel(self, task_guid: str) -> Entity: - return self._post("%s/v3/tasks/%s/actions/cancel" % (self.target_endpoint, task_guid)) + return self._post("%s/tasks/%s/actions/cancel" % (self.target_endpoint, task_guid)) diff --git a/tests/abstract_test_case.py b/tests/abstract_test_case.py index a672e0a..975fc2e 100644 --- a/tests/abstract_test_case.py +++ b/tests/abstract_test_case.py @@ -30,8 +30,6 @@ class AbstractTestCase(object): TOKEN_ENDPOINT = "http://token.somewhere.org" DOPPLER_ENDPOINT = "wss://doppler.nd-cfapi.itn.ftgroup:443" LOG_STREAM_ENDPOINT = "https://log-stream.nd-cfapi.itn.ftgroup" - API_V2_VERSION = "2.141.0" - API_V3_VERSION = "3.76.0" @classmethod def mock_client_class(cls): @@ -43,33 +41,40 @@ def build_client(self): self.client = CloudFoundryClient(self.TARGET_ENDPOINT) @staticmethod - def _mock_info_calls(requests, with_doppler: bool = True, with_log_streams: bool = True): + def _mock_info_calls( + requests, + with_doppler: bool = True, + with_log_streams: bool = True, + with_v2: bool = True, + with_v3: bool = True + ): + links = { + "self": dict(href=AbstractTestCase.TARGET_ENDPOINT), + "cloud_controller_v2": dict( + href="%s/v2" % AbstractTestCase.TARGET_ENDPOINT, + meta=dict(version="2.141.0"), + ), + "cloud_controller_v3": dict( + href="%s/v3" % AbstractTestCase.TARGET_ENDPOINT, + meta=dict(version="3.76.0"), + ), + "logging": dict(href=AbstractTestCase.DOPPLER_ENDPOINT) if with_doppler else None, + "log_stream": dict(href=AbstractTestCase.LOG_STREAM_ENDPOINT) if with_log_streams else None, + "app_ssh": dict(href="ssh.nd-cfapi.itn.ftgroup:80"), + "uaa": dict(href="https://uaa.nd-cfapi.itn.ftgroup"), + "login": dict(href=AbstractTestCase.AUTHORIZATION_ENDPOINT), + "network_policy_v0": dict(href="https://api.nd-cfapi.itn.ftgroup/networking/v0/external"), + "network_policy_v1": dict(href="https://api.nd-cfapi.itn.ftgroup/networking/v1/external"), + } + if not with_v2: + del links["cloud_controller_v2"] + if not with_v3: + del links["cloud_controller_v3"] requests.get.side_effect = [ MockResponse( "%s/" % AbstractTestCase.TARGET_ENDPOINT, status_code=HTTPStatus.OK.value, - text=json.dumps( - dict( - links={ - "self": dict(href=AbstractTestCase.TARGET_ENDPOINT), - "cloud_controller_v2": dict( - href="%s/v2" % AbstractTestCase.TARGET_ENDPOINT, - meta=dict(version=AbstractTestCase.API_V2_VERSION), - ), - "cloud_controller_v3": dict( - href="%s/v3" % AbstractTestCase.TARGET_ENDPOINT, - meta=dict(version=AbstractTestCase.API_V3_VERSION), - ), - "logging": dict(href=AbstractTestCase.DOPPLER_ENDPOINT) if with_doppler else None, - "log_stream": dict(href=AbstractTestCase.LOG_STREAM_ENDPOINT) if with_log_streams else None, - "app_ssh": dict(href="ssh.nd-cfapi.itn.ftgroup:80"), - "uaa": dict(href="https://uaa.nd-cfapi.itn.ftgroup"), - "login": dict(href=AbstractTestCase.AUTHORIZATION_ENDPOINT), - "network_policy_v0": dict(href="https://api.nd-cfapi.itn.ftgroup/networking/v0/external"), - "network_policy_v1": dict(href="https://api.nd-cfapi.itn.ftgroup/networking/v1/external"), - } - ) - ), + text=json.dumps(dict(links=links)), ), ] diff --git a/tests/test_client.py b/tests/test_client.py index b53d6de..1437d6e 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -42,6 +42,28 @@ def test_build_client_when_no_doppler(self): client = CloudFoundryClient(self.TARGET_ENDPOINT, token_format="opaque") self.assertRaises(NotImplementedError, lambda: client.doppler) + def test_build_client_when_no_v2(self): + requests = FakeRequests() + session = MockSession() + with patch("oauth2_client.credentials_manager.requests", new=requests), patch( + "cloudfoundry_client.client.requests", new=requests + ): + requests.Session.return_value = session + self._mock_info_calls(requests, with_v2=False) + client = CloudFoundryClient(self.TARGET_ENDPOINT, token_format="opaque") + self.assertRaises(NotImplementedError, lambda: client.v2) + + def test_build_client_when_no_v3(self): + requests = FakeRequests() + session = MockSession() + with patch("oauth2_client.credentials_manager.requests", new=requests), patch( + "cloudfoundry_client.client.requests", new=requests + ): + requests.Session.return_value = session + self._mock_info_calls(requests, with_v3=False) + client = CloudFoundryClient(self.TARGET_ENDPOINT, token_format="opaque") + self.assertRaises(NotImplementedError, lambda: client.v3) + def test_grant_password_request_with_token_format_opaque(self): requests = FakeRequests() session = MockSession() @@ -162,7 +184,8 @@ def test_get_info(self): self._mock_info_calls(requests) info = client._get_info(self.TARGET_ENDPOINT) self.assertEqual(info.api_endpoint, self.TARGET_ENDPOINT) - self.assertEqual(info.api_v2_version, self.API_V2_VERSION) + self.assertEqual(info.api_v2_url, "%s/v2" % self.TARGET_ENDPOINT) + self.assertEqual(info.api_v3_url, "%s/v3" % self.TARGET_ENDPOINT) self.assertEqual(info.doppler_endpoint, self.DOPPLER_ENDPOINT) self.assertEqual(info.log_stream_endpoint, self.LOG_STREAM_ENDPOINT) From 82e6fe2311f9ff780bbd5519d3ef942b7fff2599 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Mon, 6 Jan 2025 12:54:09 +0100 Subject: [PATCH 185/264] feature(dependencies) add twine --- main/__init__.py | 0 poetry.lock | 536 ++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 3 files changed, 536 insertions(+), 1 deletion(-) delete mode 100644 main/__init__.py diff --git a/main/__init__.py b/main/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/poetry.lock b/poetry.lock index 6ff95e0..cd99825 100644 --- a/poetry.lock +++ b/poetry.lock @@ -153,6 +153,21 @@ docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphi tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] +[[package]] +name = "backports-tarfile" +version = "1.2.0" +description = "Backport of CPython tarfile module" +optional = false +python-versions = ">=3.8" +files = [ + {file = "backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34"}, + {file = "backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["jaraco.test", "pytest (!=8.0.*)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)"] + [[package]] name = "black" version = "24.10.0" @@ -210,6 +225,85 @@ files = [ {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, ] +[[package]] +name = "cffi" +version = "1.17.1" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, + {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, + {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, + {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, + {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, + {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, + {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, + {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, + {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, + {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, + {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, + {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, + {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, + {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, + {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, + {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, +] + +[package.dependencies] +pycparser = "*" + [[package]] name = "charset-normalizer" version = "3.4.1" @@ -336,6 +430,66 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "cryptography" +version = "43.0.3" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e"}, + {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e"}, + {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f"}, + {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6"}, + {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18"}, + {file = "cryptography-43.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd"}, + {file = "cryptography-43.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73"}, + {file = "cryptography-43.0.3-cp37-abi3-win32.whl", hash = "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2"}, + {file = "cryptography-43.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd"}, + {file = "cryptography-43.0.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984"}, + {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5"}, + {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4"}, + {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7"}, + {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405"}, + {file = "cryptography-43.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16"}, + {file = "cryptography-43.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73"}, + {file = "cryptography-43.0.3-cp39-abi3-win32.whl", hash = "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995"}, + {file = "cryptography-43.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362"}, + {file = "cryptography-43.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d03b5621a135bffecad2c73e9f4deb1a0f977b9a8ffe6f8e002bf6c9d07b918c"}, + {file = "cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a2a431ee15799d6db9fe80c82b055bae5a752bef645bba795e8e52687c69efe3"}, + {file = "cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:281c945d0e28c92ca5e5930664c1cefd85efe80e5c0d2bc58dd63383fda29f83"}, + {file = "cryptography-43.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f18c716be16bc1fea8e95def49edf46b82fccaa88587a45f8dc0ff6ab5d8e0a7"}, + {file = "cryptography-43.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4a02ded6cd4f0a5562a8887df8b3bd14e822a90f97ac5e544c162899bc467664"}, + {file = "cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:53a583b6637ab4c4e3591a15bc9db855b8d9dee9a669b550f311480acab6eb08"}, + {file = "cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1ec0bcf7e17c0c5669d881b1cd38c4972fade441b27bda1051665faaa89bdcaa"}, + {file = "cryptography-43.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2ce6fae5bdad59577b44e4dfed356944fbf1d925269114c28be377692643b4ff"}, + {file = "cryptography-43.0.3.tar.gz", hash = "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "cryptography-vectors (==43.0.3)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "docutils" +version = "0.21.2" +description = "Docutils -- Python Documentation Utilities" +optional = false +python-versions = ">=3.9" +files = [ + {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, + {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, +] + [[package]] name = "exceptiongroup" version = "1.2.2" @@ -481,6 +635,29 @@ files = [ [package.extras] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] +[[package]] +name = "importlib-metadata" +version = "8.5.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, + {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, +] + +[package.dependencies] +zipp = ">=3.20" + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +perf = ["ipython"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["pytest-mypy"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -492,6 +669,132 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "jaraco-classes" +version = "3.4.0" +description = "Utility functions for Python class constructs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790"}, + {file = "jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd"}, +] + +[package.dependencies] +more-itertools = "*" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"] + +[[package]] +name = "jaraco-context" +version = "6.0.1" +description = "Useful decorators and context managers" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jaraco.context-6.0.1-py3-none-any.whl", hash = "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4"}, + {file = "jaraco_context-6.0.1.tar.gz", hash = "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3"}, +] + +[package.dependencies] +"backports.tarfile" = {version = "*", markers = "python_version < \"3.12\""} + +[package.extras] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +test = ["portend", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"] + +[[package]] +name = "jaraco-functools" +version = "4.1.0" +description = "Functools like those found in stdlib" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jaraco.functools-4.1.0-py3-none-any.whl", hash = "sha256:ad159f13428bc4acbf5541ad6dec511f91573b90fba04df61dafa2a1231cf649"}, + {file = "jaraco_functools-4.1.0.tar.gz", hash = "sha256:70f7e0e2ae076498e212562325e805204fc092d7b4c17e0e86c959e249701a9d"}, +] + +[package.dependencies] +more-itertools = "*" + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["jaraco.classes", "pytest (>=6,!=8.1.*)"] +type = ["pytest-mypy"] + +[[package]] +name = "jeepney" +version = "0.8.0" +description = "Low-level, pure Python DBus protocol wrapper." +optional = false +python-versions = ">=3.7" +files = [ + {file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"}, + {file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"}, +] + +[package.extras] +test = ["async-timeout", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"] +trio = ["async_generator", "trio"] + +[[package]] +name = "keyring" +version = "25.6.0" +description = "Store and access your passwords safely." +optional = false +python-versions = ">=3.9" +files = [ + {file = "keyring-25.6.0-py3-none-any.whl", hash = "sha256:552a3f7af126ece7ed5c89753650eec89c7eaae8617d0aa4d9ad2b75111266bd"}, + {file = "keyring-25.6.0.tar.gz", hash = "sha256:0b39998aa941431eb3d9b0d4b2460bc773b9df6fed7621c2dfb291a7e0187a66"}, +] + +[package.dependencies] +importlib_metadata = {version = ">=4.11.4", markers = "python_version < \"3.12\""} +"jaraco.classes" = "*" +"jaraco.context" = "*" +"jaraco.functools" = "*" +jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""} +pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""} +SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +completion = ["shtab (>=1.1.0)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["pyfakefs", "pytest (>=6,!=8.1.*)"] +type = ["pygobject-stubs", "pytest-mypy", "shtab", "types-pywin32"] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + [[package]] name = "mccabe" version = "0.7.0" @@ -503,6 +806,28 @@ files = [ {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "more-itertools" +version = "10.5.0" +description = "More routines for operating on iterables, beyond itertools" +optional = false +python-versions = ">=3.8" +files = [ + {file = "more-itertools-10.5.0.tar.gz", hash = "sha256:5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6"}, + {file = "more_itertools-10.5.0-py3-none-any.whl", hash = "sha256:037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef"}, +] + [[package]] name = "multidict" version = "6.1.0" @@ -618,6 +943,39 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "nh3" +version = "0.2.20" +description = "Python binding to Ammonia HTML sanitizer Rust crate" +optional = false +python-versions = ">=3.8" +files = [ + {file = "nh3-0.2.20-cp313-cp313t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e1061a4ab6681f6bdf72b110eea0c4e1379d57c9de937db3be4202f7ad6043db"}, + {file = "nh3-0.2.20-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb4254b1dac4a1ee49919a5b3f1caf9803ea8dada1816d9e8289e63d3cd0dd9a"}, + {file = "nh3-0.2.20-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ae9cbd713524cdb81e64663d0d6aae26f678db9f2cd9db0bf162606f1f9f20c"}, + {file = "nh3-0.2.20-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e1f7370b4e14cc03f5ae141ef30a1caf81fa5787711f80be9081418dd9eb79d2"}, + {file = "nh3-0.2.20-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:ac4d27dc836a476efffc6eb661994426b8b805c951b29c9cf2ff36bc9ad58bc5"}, + {file = "nh3-0.2.20-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:4fd2e9248725ebcedac3997a8d3da0d90a12a28c9179c6ba51f1658938ac30d0"}, + {file = "nh3-0.2.20-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f7d564871833ddbe54df3aa59053b1110729d3a800cb7628ae8f42adb3d75208"}, + {file = "nh3-0.2.20-cp313-cp313t-win32.whl", hash = "sha256:d2a176fd4306b6f0f178a3f67fac91bd97a3a8d8fafb771c9b9ef675ba5c8886"}, + {file = "nh3-0.2.20-cp313-cp313t-win_amd64.whl", hash = "sha256:6ed834c68452a600f517dd3e1534dbfaff1f67f98899fecf139a055a25d99150"}, + {file = "nh3-0.2.20-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:76e2f603b30c02ff6456b233a83fc377dedab6a50947b04e960a6b905637b776"}, + {file = "nh3-0.2.20-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:181063c581defe683bd4bb78188ac9936d208aebbc74c7f7c16b6a32ae2ebb38"}, + {file = "nh3-0.2.20-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:231addb7643c952cd6d71f1c8702d703f8fe34afcb20becb3efb319a501a12d7"}, + {file = "nh3-0.2.20-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1b9a8340a0aab991c68a5ca938d35ef4a8a3f4bf1b455da8855a40bee1fa0ace"}, + {file = "nh3-0.2.20-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10317cd96fe4bbd4eb6b95f3920b71c902157ad44fed103fdcde43e3b8ee8be6"}, + {file = "nh3-0.2.20-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8698db4c04b140800d1a1cd3067fda399e36e1e2b8fc1fe04292a907350a3e9b"}, + {file = "nh3-0.2.20-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eb04b9c3deb13c3a375ea39fd4a3c00d1f92e8fb2349f25f1e3e4506751774b"}, + {file = "nh3-0.2.20-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92f3f1c4f47a2c6f3ca7317b1d5ced05bd29556a75d3a4e2715652ae9d15c05d"}, + {file = "nh3-0.2.20-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ddefa9fd6794a87e37d05827d299d4b53a3ec6f23258101907b96029bfef138a"}, + {file = "nh3-0.2.20-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ce3731c8f217685d33d9268362e5b4f770914e922bba94d368ab244a59a6c397"}, + {file = "nh3-0.2.20-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:09f037c02fc2c43b211ff1523de32801dcfb0918648d8e651c36ef890f1731ec"}, + {file = "nh3-0.2.20-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:813f1c8012dd64c990514b795508abb90789334f76a561fa0fd4ca32d2275330"}, + {file = "nh3-0.2.20-cp38-abi3-win32.whl", hash = "sha256:47b2946c0e13057855209daeffb45dc910bd0c55daf10190bb0b4b60e2999784"}, + {file = "nh3-0.2.20-cp38-abi3-win_amd64.whl", hash = "sha256:da87573f03084edae8eb87cfe811ec338606288f81d333c07d2a9a0b9b976c0b"}, + {file = "nh3-0.2.20.tar.gz", hash = "sha256:9705c42d7ff88a0bea546c82d7fe5e59135e3d3f057e485394f491248a1f8ed5"}, +] + [[package]] name = "oauth2-client" version = "1.4.2" @@ -654,6 +1012,20 @@ files = [ {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] +[[package]] +name = "pkginfo" +version = "1.12.0" +description = "Query metadata from sdists / bdists / installed packages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pkginfo-1.12.0-py3-none-any.whl", hash = "sha256:dcd589c9be4da8973eceffa247733c144812759aa67eaf4bbf97016a02f39088"}, + {file = "pkginfo-1.12.0.tar.gz", hash = "sha256:8ad91a0445a036782b9366ef8b8c2c50291f83a553478ba8580c73d3215700cf"}, +] + +[package.extras] +testing = ["pytest", "pytest-cov", "wheel"] + [[package]] name = "platformdirs" version = "4.3.6" @@ -818,6 +1190,17 @@ files = [ {file = "pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"}, ] +[[package]] +name = "pycparser" +version = "2.22" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, +] + [[package]] name = "pyflakes" version = "3.2.0" @@ -829,6 +1212,20 @@ files = [ {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, ] +[[package]] +name = "pygments" +version = "2.19.0" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pygments-2.19.0-py3-none-any.whl", hash = "sha256:4755e6e64d22161d5b61432c0600c923c5927214e7c956e31c23923c89251a9b"}, + {file = "pygments-2.19.0.tar.gz", hash = "sha256:afc4146269910d4bdfabcd27c24923137a74d562a23a320a41a55ad303e19783"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + [[package]] name = "pytest" version = "8.2.2" @@ -851,6 +1248,17 @@ tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +[[package]] +name = "pywin32-ctypes" +version = "0.2.3" +description = "A (partial) reimplementation of pywin32 using ctypes/cffi" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755"}, + {file = "pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8"}, +] + [[package]] name = "pyyaml" version = "6.0.2" @@ -913,6 +1321,25 @@ files = [ {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] +[[package]] +name = "readme-renderer" +version = "44.0" +description = "readme_renderer is a library for rendering readme descriptions for Warehouse" +optional = false +python-versions = ">=3.9" +files = [ + {file = "readme_renderer-44.0-py3-none-any.whl", hash = "sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151"}, + {file = "readme_renderer-44.0.tar.gz", hash = "sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1"}, +] + +[package.dependencies] +docutils = ">=0.21.2" +nh3 = ">=0.2.14" +Pygments = ">=2.5.1" + +[package.extras] +md = ["cmarkgfm (>=0.8.0)"] + [[package]] name = "requests" version = "2.32.3" @@ -934,6 +1361,68 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "requests-toolbelt" +version = "1.0.0" +description = "A utility belt for advanced users of python-requests" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, + {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, +] + +[package.dependencies] +requests = ">=2.0.1,<3.0.0" + +[[package]] +name = "rfc3986" +version = "2.0.0" +description = "Validating URI References per RFC 3986" +optional = false +python-versions = ">=3.7" +files = [ + {file = "rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd"}, + {file = "rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c"}, +] + +[package.extras] +idna2008 = ["idna"] + +[[package]] +name = "rich" +version = "13.9.4" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, + {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""} + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "secretstorage" +version = "3.3.3" +description = "Python bindings to FreeDesktop.org Secret Service API" +optional = false +python-versions = ">=3.6" +files = [ + {file = "SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"}, + {file = "SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"}, +] + +[package.dependencies] +cryptography = ">=2.0" +jeepney = ">=0.6" + [[package]] name = "tomli" version = "2.2.1" @@ -975,6 +1464,32 @@ files = [ {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] +[[package]] +name = "twine" +version = "6.0.1" +description = "Collection of utilities for publishing packages on PyPI" +optional = false +python-versions = ">=3.8" +files = [ + {file = "twine-6.0.1-py3-none-any.whl", hash = "sha256:9c6025b203b51521d53e200f4a08b116dee7500a38591668c6a6033117bdc218"}, + {file = "twine-6.0.1.tar.gz", hash = "sha256:36158b09df5406e1c9c1fb8edb24fc2be387709443e7376689b938531582ee27"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""} +keyring = {version = ">=15.1", markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\""} +packaging = "*" +pkginfo = ">=1.8.1" +readme-renderer = ">=35.0" +requests = ">=2.20" +requests-toolbelt = ">=0.8.0,<0.9.0 || >0.9.0" +rfc3986 = ">=1.4.0" +rich = ">=12.0.0" +urllib3 = ">=1.26.0" + +[package.extras] +keyring = ["keyring (>=15.1)"] + [[package]] name = "typing-extensions" version = "4.12.2" @@ -1115,7 +1630,26 @@ idna = ">=2.0" multidict = ">=4.0" propcache = ">=0.2.0" +[[package]] +name = "zipp" +version = "3.21.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.9" +files = [ + {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, + {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] + [metadata] lock-version = "2.0" python-versions = ">=3.9" -content-hash = "1a7ece3e142ed461135a07c5000dd043a1ea9a517e7199c3c93e0788d481119b" +content-hash = "7bdbca53bb1649a03058590ca7d26086d9c1a224a23e87a68a7e570cde860283" diff --git a/pyproject.toml b/pyproject.toml index be25d17..49020cb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,7 @@ polling2= "0.5.0" black= "24.10.0" flake8= "7.1.1" pytest = "~8.2.2" +twine = "~6.0.0" [tool.poetry.scripts] cloudfoundry-client = "cloudfoundry_client.main.main:main" From a91c06ecaf0c773351dff9be5f66643567373a38 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Mon, 6 Jan 2025 12:54:56 +0100 Subject: [PATCH 186/264] release - prepare 1.38.0 --- cloudfoundry_client/__init__.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudfoundry_client/__init__.py b/cloudfoundry_client/__init__.py index 5740c67..c141030 100644 --- a/cloudfoundry_client/__init__.py +++ b/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.37.1" +__version__ = "1.38.0" diff --git a/pyproject.toml b/pyproject.toml index 49020cb..cbd9a23 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ exclude = ''' [tool.poetry] name = "cloudfoundry_client" -version = "1.37.1" +version = "1.38.0" description = "A client library for CloudFoundry" authors = ["Benjamin Einaudi "] readme = "README.rst" From 0d8473cc3ff671631bfa8f81cf2f890918f979a3 Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Tue, 14 Jan 2025 08:06:24 +0100 Subject: [PATCH 187/264] fixes(navigation) put back /v2 and /v3 in path managers Close #224 --- cloudfoundry_client/__init__.py | 2 +- cloudfoundry_client/client.py | 22 ++++++++++--------- cloudfoundry_client/v2/apps.py | 2 +- cloudfoundry_client/v2/buildpacks.py | 2 +- cloudfoundry_client/v2/events.py | 2 +- cloudfoundry_client/v2/jobs.py | 2 +- cloudfoundry_client/v2/resources.py | 2 +- cloudfoundry_client/v2/routes.py | 2 +- cloudfoundry_client/v2/service_bindings.py | 2 +- cloudfoundry_client/v2/service_brokers.py | 2 +- cloudfoundry_client/v2/service_instances.py | 2 +- cloudfoundry_client/v2/service_keys.py | 2 +- .../v2/service_plan_visibilities.py | 2 +- cloudfoundry_client/v2/service_plans.py | 2 +- cloudfoundry_client/v2/spaces.py | 2 +- cloudfoundry_client/v3/apps.py | 2 +- cloudfoundry_client/v3/buildpacks.py | 2 +- cloudfoundry_client/v3/domains.py | 4 ++-- cloudfoundry_client/v3/feature_flags.py | 2 +- cloudfoundry_client/v3/isolation_segments.py | 2 +- cloudfoundry_client/v3/jobs.py | 2 +- cloudfoundry_client/v3/organization_quotas.py | 2 +- cloudfoundry_client/v3/organizations.py | 2 +- cloudfoundry_client/v3/processes.py | 2 +- cloudfoundry_client/v3/roles.py | 2 +- cloudfoundry_client/v3/security_groups.py | 2 +- cloudfoundry_client/v3/service_brokers.py | 2 +- .../v3/service_credential_bindings.py | 2 +- cloudfoundry_client/v3/service_instances.py | 2 +- cloudfoundry_client/v3/service_offerings.py | 2 +- cloudfoundry_client/v3/service_plans.py | 2 +- cloudfoundry_client/v3/spaces.py | 2 +- cloudfoundry_client/v3/tasks.py | 6 ++--- pyproject.toml | 2 +- 34 files changed, 48 insertions(+), 46 deletions(-) diff --git a/cloudfoundry_client/__init__.py b/cloudfoundry_client/__init__.py index c141030..0767387 100644 --- a/cloudfoundry_client/__init__.py +++ b/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.38.0" +__version__ = "1.38.1" diff --git a/cloudfoundry_client/client.py b/cloudfoundry_client/client.py index 7794741..6c85d5f 100644 --- a/cloudfoundry_client/client.py +++ b/cloudfoundry_client/client.py @@ -81,7 +81,8 @@ def __init__(self, target_endpoint: str, credential_manager: "CloudFoundryClient class V2(object): - def __init__(self, target_endpoint: str, credential_manager: "CloudFoundryClient"): + def __init__(self, cloud_controller_v2_url: str, credential_manager: "CloudFoundryClient"): + target_endpoint = cloud_controller_v2_url.removesuffix("/v2") self.apps = AppManagerV2(target_endpoint, credential_manager) self.buildpacks = BuildpackManagerV2(target_endpoint, credential_manager) self.jobs = JobManagerV2(target_endpoint, credential_manager) @@ -93,24 +94,25 @@ def __init__(self, target_endpoint: str, credential_manager: "CloudFoundryClient self.service_plans = ServicePlanManagerV2(target_endpoint, credential_manager) # Default implementations self.event = EventManager(target_endpoint, credential_manager) - self.organizations = EntityManagerV2(target_endpoint, credential_manager, "/organizations") - self.private_domains = EntityManagerV2(target_endpoint, credential_manager, "/private_domains") + self.organizations = EntityManagerV2(target_endpoint, credential_manager, "/v2/organizations") + self.private_domains = EntityManagerV2(target_endpoint, credential_manager, "/v2/private_domains") self.routes = RouteManager(target_endpoint, credential_manager) - self.services = EntityManagerV2(target_endpoint, credential_manager, "/services") - self.shared_domains = EntityManagerV2(target_endpoint, credential_manager, "/shared_domains") + self.services = EntityManagerV2(target_endpoint, credential_manager, "/v2/services") + self.shared_domains = EntityManagerV2(target_endpoint, credential_manager, "/v2/shared_domains") self.spaces = SpaceManagerV2(target_endpoint, credential_manager) - self.stacks = EntityManagerV2(target_endpoint, credential_manager, "/stacks") + self.stacks = EntityManagerV2(target_endpoint, credential_manager, "/v2/stacks") self.user_provided_service_instances = EntityManagerV2( - target_endpoint, credential_manager, "/user_provided_service_instances" + target_endpoint, credential_manager, "/v2/user_provided_service_instances" ) - self.security_groups = EntityManagerV2(target_endpoint, credential_manager, "/security_groups") - self.users = EntityManagerV2(target_endpoint, credential_manager, "/users") + self.security_groups = EntityManagerV2(target_endpoint, credential_manager, "/v2/security_groups") + self.users = EntityManagerV2(target_endpoint, credential_manager, "/v2/users") # Resources implementation used by push operation self.resources = ResourceManager(target_endpoint, credential_manager) class V3(object): - def __init__(self, target_endpoint: str, credential_manager: "CloudFoundryClient"): + def __init__(self, cloud_controller_v3_url: str, credential_manager: "CloudFoundryClient"): + target_endpoint = cloud_controller_v3_url.removesuffix("/v3") self.apps = AppManager(target_endpoint, credential_manager) self.buildpacks = BuildpackManager(target_endpoint, credential_manager) self.domains = DomainManager(target_endpoint, credential_manager) diff --git a/cloudfoundry_client/v2/apps.py b/cloudfoundry_client/v2/apps.py index d060bff..cf68ed8 100644 --- a/cloudfoundry_client/v2/apps.py +++ b/cloudfoundry_client/v2/apps.py @@ -77,7 +77,7 @@ class AppManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(AppManager, self).__init__( - target_endpoint, client, "/apps", lambda pairs: Application(target_endpoint, client, pairs) + target_endpoint, client, "/v2/apps", lambda pairs: Application(target_endpoint, client, pairs) ) def get_stats(self, application_guid: str) -> Dict[str, JsonObject]: diff --git a/cloudfoundry_client/v2/buildpacks.py b/cloudfoundry_client/v2/buildpacks.py index 8e1276b..958a283 100644 --- a/cloudfoundry_client/v2/buildpacks.py +++ b/cloudfoundry_client/v2/buildpacks.py @@ -8,7 +8,7 @@ class BuildpackManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(BuildpackManager, self).__init__(target_endpoint, client, "/buildpacks") + super(BuildpackManager, self).__init__(target_endpoint, client, "/v2/buildpacks") def update(self, buildpack_guid: str, parameters: dict) -> Entity: return super(BuildpackManager, self)._update(buildpack_guid, parameters) diff --git a/cloudfoundry_client/v2/events.py b/cloudfoundry_client/v2/events.py index cdec234..9822165 100644 --- a/cloudfoundry_client/v2/events.py +++ b/cloudfoundry_client/v2/events.py @@ -8,7 +8,7 @@ class EventManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(EventManager, self).__init__(target_endpoint, client, "/events") + super(EventManager, self).__init__(target_endpoint, client, "/v2/events") def list_by_type(self, event_type: str) -> Generator[Entity, None, None]: return self._list(self.entity_uri, type=event_type) diff --git a/cloudfoundry_client/v2/jobs.py b/cloudfoundry_client/v2/jobs.py index f1a6f53..68bdaed 100644 --- a/cloudfoundry_client/v2/jobs.py +++ b/cloudfoundry_client/v2/jobs.py @@ -12,4 +12,4 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): self.client = client def get(self, job_guid: str) -> JsonObject: - return self.client.get("%s/jobs/%s" % (self.target_endpoint, job_guid)).json(object_pairs_hook=JsonObject) + return self.client.get("%s/v2/jobs/%s" % (self.target_endpoint, job_guid)).json(object_pairs_hook=JsonObject) diff --git a/cloudfoundry_client/v2/resources.py b/cloudfoundry_client/v2/resources.py index 2b0dae3..d962daa 100644 --- a/cloudfoundry_client/v2/resources.py +++ b/cloudfoundry_client/v2/resources.py @@ -12,5 +12,5 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): self.client = client def match(self, items: List[dict]) -> List[JsonObject]: - response = self.client.put("%s/resource_match" % self.client.info.api_endpoint, json=items) + response = self.client.put("%s/v2/resource_match" % self.client.info.api_endpoint, json=items) return response.json(object_pairs_hook=JsonObject) diff --git a/cloudfoundry_client/v2/routes.py b/cloudfoundry_client/v2/routes.py index bf9d425..1e4d52c 100644 --- a/cloudfoundry_client/v2/routes.py +++ b/cloudfoundry_client/v2/routes.py @@ -8,7 +8,7 @@ class RouteManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(RouteManager, self).__init__(target_endpoint, client, "/routes") + super(RouteManager, self).__init__(target_endpoint, client, "/v2/routes") def create_tcp_route(self, domain_guid: str, space_guid: str, port: Optional[int] = None) -> Entity: request = self._request(domain_guid=domain_guid, space_guid=space_guid) diff --git a/cloudfoundry_client/v2/service_bindings.py b/cloudfoundry_client/v2/service_bindings.py index 95f9f5b..00fc625 100644 --- a/cloudfoundry_client/v2/service_bindings.py +++ b/cloudfoundry_client/v2/service_bindings.py @@ -8,7 +8,7 @@ class ServiceBindingManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceBindingManager, self).__init__(target_endpoint, client, "/service_bindings") + super(ServiceBindingManager, self).__init__(target_endpoint, client, "/v2/service_bindings") def create(self, app_guid: str, instance_guid: str, parameters: Optional[dict] = None, name: Optional[str] = None) -> Entity: request = self._request(app_guid=app_guid, service_instance_guid=instance_guid) diff --git a/cloudfoundry_client/v2/service_brokers.py b/cloudfoundry_client/v2/service_brokers.py index afc2c1a..abe44c7 100644 --- a/cloudfoundry_client/v2/service_brokers.py +++ b/cloudfoundry_client/v2/service_brokers.py @@ -8,7 +8,7 @@ class ServiceBrokerManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceBrokerManager, self).__init__(target_endpoint, client, "/service_brokers") + super(ServiceBrokerManager, self).__init__(target_endpoint, client, "/v2/service_brokers") def create( self, broker_url: str, broker_name: str, auth_username: str, auth_password: str, space_guid: Optional[str] = None diff --git a/cloudfoundry_client/v2/service_instances.py b/cloudfoundry_client/v2/service_instances.py index edeac74..f17fa7f 100644 --- a/cloudfoundry_client/v2/service_instances.py +++ b/cloudfoundry_client/v2/service_instances.py @@ -10,7 +10,7 @@ class ServiceInstanceManager(EntityManager): list_query_parameters = ["page", "results-per-page", "order-direction", "return_user_provided_service_instances"] def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceInstanceManager, self).__init__(target_endpoint, client, "/service_instances") + super(ServiceInstanceManager, self).__init__(target_endpoint, client, "/v2/service_instances") def create( self, diff --git a/cloudfoundry_client/v2/service_keys.py b/cloudfoundry_client/v2/service_keys.py index 4f4da97..cb87c3e 100644 --- a/cloudfoundry_client/v2/service_keys.py +++ b/cloudfoundry_client/v2/service_keys.py @@ -8,7 +8,7 @@ class ServiceKeyManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceKeyManager, self).__init__(target_endpoint, client, "/service_keys") + super(ServiceKeyManager, self).__init__(target_endpoint, client, "/v2/service_keys") def create(self, service_instance_guid: str, name: str, parameters: Optional[dict] = None) -> Entity: request = self._request(service_instance_guid=service_instance_guid, name=name) diff --git a/cloudfoundry_client/v2/service_plan_visibilities.py b/cloudfoundry_client/v2/service_plan_visibilities.py index 47bcedf..4d7d513 100644 --- a/cloudfoundry_client/v2/service_plan_visibilities.py +++ b/cloudfoundry_client/v2/service_plan_visibilities.py @@ -8,7 +8,7 @@ class ServicePlanVisibilityManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServicePlanVisibilityManager, self).__init__(target_endpoint, client, "/service_plan_visibilities") + super(ServicePlanVisibilityManager, self).__init__(target_endpoint, client, "/v2/service_plan_visibilities") def create(self, service_plan_guid: str, organization_guid: str) -> Entity: request = self._request() diff --git a/cloudfoundry_client/v2/service_plans.py b/cloudfoundry_client/v2/service_plans.py index 8c8e766..9a94aa8 100644 --- a/cloudfoundry_client/v2/service_plans.py +++ b/cloudfoundry_client/v2/service_plans.py @@ -9,7 +9,7 @@ class ServicePlanManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServicePlanManager, self).__init__(target_endpoint, client, "/service_plans") + super(ServicePlanManager, self).__init__(target_endpoint, client, "/v2/service_plans") def create_from_resource_file(self, path: str) -> Entity: raise NotImplementedError("No creation allowed") diff --git a/cloudfoundry_client/v2/spaces.py b/cloudfoundry_client/v2/spaces.py index b00743c..6f6254a 100644 --- a/cloudfoundry_client/v2/spaces.py +++ b/cloudfoundry_client/v2/spaces.py @@ -8,7 +8,7 @@ class SpaceManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(SpaceManager, self).__init__(target_endpoint, client, "/spaces") + super(SpaceManager, self).__init__(target_endpoint, client, "/v2/spaces") def delete_unmapped_routes(self, space_guid: str): url = "%s%s/%s/unmapped_routes" % (self.target_endpoint, self.entity_uri, space_guid) diff --git a/cloudfoundry_client/v3/apps.py b/cloudfoundry_client/v3/apps.py index 338c86c..1453575 100644 --- a/cloudfoundry_client/v3/apps.py +++ b/cloudfoundry_client/v3/apps.py @@ -9,7 +9,7 @@ class AppManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(AppManager, self).__init__(target_endpoint, client, "/apps") + super(AppManager, self).__init__(target_endpoint, client, "/v3/apps") def restart(self, application_guid: str): return super(AppManager, self)._post("%s%s/%s/actions/restart" % (self.target_endpoint, diff --git a/cloudfoundry_client/v3/buildpacks.py b/cloudfoundry_client/v3/buildpacks.py index f8930b2..f6a1284 100644 --- a/cloudfoundry_client/v3/buildpacks.py +++ b/cloudfoundry_client/v3/buildpacks.py @@ -8,7 +8,7 @@ class BuildpackManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(BuildpackManager, self).__init__(target_endpoint, client, "/buildpacks") + super(BuildpackManager, self).__init__(target_endpoint, client, "/v3/buildpacks") def create( self, diff --git a/cloudfoundry_client/v3/domains.py b/cloudfoundry_client/v3/domains.py index 0077115..d1f5db3 100644 --- a/cloudfoundry_client/v3/domains.py +++ b/cloudfoundry_client/v3/domains.py @@ -21,7 +21,7 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient", **kwargs) class DomainManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(DomainManager, self).__init__(target_endpoint, client, "/domains", Domain) + super(DomainManager, self).__init__(target_endpoint, client, "/v3/domains", Domain) def create( self, @@ -44,7 +44,7 @@ def create( return super(DomainManager, self)._create(data) def list_domains_for_org(self, org_guid: str, **kwargs) -> Pagination[Entity]: - uri = "/organizations/{guid}/domains".format(guid=org_guid) + uri = "/v3/organizations/{guid}/domains".format(guid=org_guid) return self._list(uri, **kwargs) def update(self, domain_guid: str, meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Domain: diff --git a/cloudfoundry_client/v3/feature_flags.py b/cloudfoundry_client/v3/feature_flags.py index 3c280ad..47c35d8 100644 --- a/cloudfoundry_client/v3/feature_flags.py +++ b/cloudfoundry_client/v3/feature_flags.py @@ -8,7 +8,7 @@ class FeatureFlagManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(FeatureFlagManager, self).__init__(target_endpoint, client, "/feature_flags") + super(FeatureFlagManager, self).__init__(target_endpoint, client, "/v3/feature_flags") def update(self, name: str, enabled: Optional[bool] = True, custom_error_message: Optional[str] = None) -> Entity: data = {"enabled": enabled, "custom_error_message": custom_error_message} diff --git a/cloudfoundry_client/v3/isolation_segments.py b/cloudfoundry_client/v3/isolation_segments.py index 391e4d5..67fa114 100644 --- a/cloudfoundry_client/v3/isolation_segments.py +++ b/cloudfoundry_client/v3/isolation_segments.py @@ -8,7 +8,7 @@ class IsolationSegmentManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(IsolationSegmentManager, self).__init__(target_endpoint, client, "/isolation_segments") + super(IsolationSegmentManager, self).__init__(target_endpoint, client, "/v3/isolation_segments") def create(self, name: str, meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Entity: data = {"name": name, "metadata": {"labels": meta_labels, "annotations": meta_annotations}} diff --git a/cloudfoundry_client/v3/jobs.py b/cloudfoundry_client/v3/jobs.py index 5fd10fb..80bd510 100644 --- a/cloudfoundry_client/v3/jobs.py +++ b/cloudfoundry_client/v3/jobs.py @@ -15,7 +15,7 @@ class JobTimeout(Exception): class JobManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(JobManager, self).__init__(target_endpoint, client, "/jobs") + super(JobManager, self).__init__(target_endpoint, client, "/v3/jobs") def wait_for_job_completion( self, diff --git a/cloudfoundry_client/v3/organization_quotas.py b/cloudfoundry_client/v3/organization_quotas.py index 2779f9c..abe5015 100644 --- a/cloudfoundry_client/v3/organization_quotas.py +++ b/cloudfoundry_client/v3/organization_quotas.py @@ -35,7 +35,7 @@ class DomainsQuota: class OrganizationQuotaManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super().__init__(target_endpoint, client, "/organization_quotas") + super().__init__(target_endpoint, client, "/v3/organization_quotas") def remove(self, guid: str, asynchronous: bool = True) -> Optional[str]: return super()._remove(guid, asynchronous) diff --git a/cloudfoundry_client/v3/organizations.py b/cloudfoundry_client/v3/organizations.py index e7b4639..2086d31 100644 --- a/cloudfoundry_client/v3/organizations.py +++ b/cloudfoundry_client/v3/organizations.py @@ -8,7 +8,7 @@ class OrganizationManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(OrganizationManager, self).__init__(target_endpoint, client, "/organizations") + super(OrganizationManager, self).__init__(target_endpoint, client, "/v3/organizations") def create( self, name: str, suspended: bool, meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None diff --git a/cloudfoundry_client/v3/processes.py b/cloudfoundry_client/v3/processes.py index f7ee291..cb632f3 100644 --- a/cloudfoundry_client/v3/processes.py +++ b/cloudfoundry_client/v3/processes.py @@ -8,4 +8,4 @@ class ProcessManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ProcessManager, self).__init__(target_endpoint, client, "/processes") + super(ProcessManager, self).__init__(target_endpoint, client, "/v3/processes") diff --git a/cloudfoundry_client/v3/roles.py b/cloudfoundry_client/v3/roles.py index ed466b9..9123d5a 100644 --- a/cloudfoundry_client/v3/roles.py +++ b/cloudfoundry_client/v3/roles.py @@ -8,7 +8,7 @@ class RoleManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(RoleManager, self).__init__(target_endpoint, client, "/roles") + super(RoleManager, self).__init__(target_endpoint, client, "/v3/roles") def remove(self, role_guid: str, asynchronous: bool = True) -> Optional[str]: return super(RoleManager, self)._remove(role_guid, asynchronous) diff --git a/cloudfoundry_client/v3/security_groups.py b/cloudfoundry_client/v3/security_groups.py index fdde7a2..b134e03 100644 --- a/cloudfoundry_client/v3/security_groups.py +++ b/cloudfoundry_client/v3/security_groups.py @@ -37,7 +37,7 @@ class GloballyEnabled: class SecurityGroupManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(SecurityGroupManager, self).__init__(target_endpoint, client, "/security_groups") + super(SecurityGroupManager, self).__init__(target_endpoint, client, "/v3/security_groups") def create(self, name: str, diff --git a/cloudfoundry_client/v3/service_brokers.py b/cloudfoundry_client/v3/service_brokers.py index a354de9..c39795b 100644 --- a/cloudfoundry_client/v3/service_brokers.py +++ b/cloudfoundry_client/v3/service_brokers.py @@ -8,7 +8,7 @@ class ServiceBrokerManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceBrokerManager, self).__init__(target_endpoint, client, "/service_brokers") + super(ServiceBrokerManager, self).__init__(target_endpoint, client, "/v3/service_brokers") def create( self, diff --git a/cloudfoundry_client/v3/service_credential_bindings.py b/cloudfoundry_client/v3/service_credential_bindings.py index 6599686..d587b7a 100644 --- a/cloudfoundry_client/v3/service_credential_bindings.py +++ b/cloudfoundry_client/v3/service_credential_bindings.py @@ -9,7 +9,7 @@ class ServiceCredentialBindingManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(ServiceCredentialBindingManager, self).__init__(target_endpoint, client, - "/service_credential_bindings") + "/v3/service_credential_bindings") def create( self, diff --git a/cloudfoundry_client/v3/service_instances.py b/cloudfoundry_client/v3/service_instances.py index 695b0ef..ad33614 100644 --- a/cloudfoundry_client/v3/service_instances.py +++ b/cloudfoundry_client/v3/service_instances.py @@ -9,7 +9,7 @@ class ServiceInstanceManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceInstanceManager, self).__init__(target_endpoint, client, "/service_instances") + super(ServiceInstanceManager, self).__init__(target_endpoint, client, "/v3/service_instances") def create( self, diff --git a/cloudfoundry_client/v3/service_offerings.py b/cloudfoundry_client/v3/service_offerings.py index c784ca2..37f7d0c 100644 --- a/cloudfoundry_client/v3/service_offerings.py +++ b/cloudfoundry_client/v3/service_offerings.py @@ -8,7 +8,7 @@ class ServiceOfferingsManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceOfferingsManager, self).__init__(target_endpoint, client, "/service_offerings") + super(ServiceOfferingsManager, self).__init__(target_endpoint, client, "/v3/service_offerings") def update(self, guid: str, meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Entity: payload = dict() diff --git a/cloudfoundry_client/v3/service_plans.py b/cloudfoundry_client/v3/service_plans.py index 9e27e15..257c3bf 100644 --- a/cloudfoundry_client/v3/service_plans.py +++ b/cloudfoundry_client/v3/service_plans.py @@ -8,7 +8,7 @@ class ServicePlanManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServicePlanManager, self).__init__(target_endpoint, client, "/service_plans") + super(ServicePlanManager, self).__init__(target_endpoint, client, "/v3/service_plans") def update( self, diff --git a/cloudfoundry_client/v3/spaces.py b/cloudfoundry_client/v3/spaces.py index acb3597..f871a43 100644 --- a/cloudfoundry_client/v3/spaces.py +++ b/cloudfoundry_client/v3/spaces.py @@ -8,7 +8,7 @@ class SpaceManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(SpaceManager, self).__init__(target_endpoint, client, "/spaces") + super(SpaceManager, self).__init__(target_endpoint, client, "/v3/spaces") def create(self, name: str, org_guid: str) -> Entity: return super(SpaceManager, self)._create(dict(name=name, relationships=dict(organization=ToOneRelationship(org_guid)))) diff --git a/cloudfoundry_client/v3/tasks.py b/cloudfoundry_client/v3/tasks.py index 1367348..a7ce16e 100644 --- a/cloudfoundry_client/v3/tasks.py +++ b/cloudfoundry_client/v3/tasks.py @@ -8,7 +8,7 @@ class TaskManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(TaskManager, self).__init__(target_endpoint, client, "/tasks") + super(TaskManager, self).__init__(target_endpoint, client, "/v3/tasks") def create( self, @@ -24,7 +24,7 @@ def create( request["disk_in_mb"] = disk_in_mb request["memory_in_mb"] = memory_in_mb request["droplet_guid"] = droplet_guid - return self._post("%s/apps/%s/tasks" % (self.target_endpoint, application_guid), data=request) + return self._post("%s/v3/apps/%s/tasks" % (self.target_endpoint, application_guid), data=request) def cancel(self, task_guid: str) -> Entity: - return self._post("%s/tasks/%s/actions/cancel" % (self.target_endpoint, task_guid)) + return self._post("%s/v3/tasks/%s/actions/cancel" % (self.target_endpoint, task_guid)) diff --git a/pyproject.toml b/pyproject.toml index cbd9a23..1def3b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ exclude = ''' [tool.poetry] name = "cloudfoundry_client" -version = "1.38.0" +version = "1.38.1" description = "A client library for CloudFoundry" authors = ["Benjamin Einaudi "] readme = "README.rst" From ec7d14c0104f7c3f5515737bb296b1ecdbcb2974 Mon Sep 17 00:00:00 2001 From: Baron Zhang <85721607+lightedcode@users.noreply.github.com> Date: Wed, 15 Jan 2025 05:27:32 +0800 Subject: [PATCH 188/264] Fix: Ensure correct handling of `_included` entities in `Entity` class in V3 API. (#225) --- cloudfoundry_client/v3/entities.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudfoundry_client/v3/entities.py b/cloudfoundry_client/v3/entities.py index 343a8f1..f793f84 100644 --- a/cloudfoundry_client/v3/entities.py +++ b/cloudfoundry_client/v3/entities.py @@ -48,7 +48,7 @@ def _create_navigable_included_entities(self, v3_client: "V3", default_manager: other_manager = getattr(v3_client, manager_name, default_manager) entity_type = other_manager._get_entity_type(entity_name) entity = entity_type(other_manager.target_endpoint, other_manager.client, **entity_data) - setattr(self, entity_name, lambda: entity) + setattr(self, entity_name, functools.partial(lambda e: e, entity)) self.pop("_included", None) @staticmethod From 704d9982cf104601596675adef468a65904c0faf Mon Sep 17 00:00:00 2001 From: Ben Einaudi Date: Tue, 14 Jan 2025 22:29:31 +0100 Subject: [PATCH 189/264] Prepare 1.38.2 --- cloudfoundry_client/__init__.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudfoundry_client/__init__.py b/cloudfoundry_client/__init__.py index 0767387..6d29d24 100644 --- a/cloudfoundry_client/__init__.py +++ b/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.38.1" +__version__ = "1.38.2" diff --git a/pyproject.toml b/pyproject.toml index 1def3b5..605d4af 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ exclude = ''' [tool.poetry] name = "cloudfoundry_client" -version = "1.38.1" +version = "1.38.2" description = "A client library for CloudFoundry" authors = ["Benjamin Einaudi "] readme = "README.rst" From 03bb298edbdae3801d9e1b6fe7d85b600a552d1c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 08:29:27 +0100 Subject: [PATCH 190/264] Bump twine from 6.0.1 to 6.1.0 (#227) Bumps [twine](https://github.com/pypa/twine) from 6.0.1 to 6.1.0. - [Release notes](https://github.com/pypa/twine/releases) - [Changelog](https://github.com/pypa/twine/blob/main/docs/changelog.rst) - [Commits](https://github.com/pypa/twine/compare/6.0.1...6.1.0) --- updated-dependencies: - dependency-name: twine dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 45 +++++++++++++++++++++++++-------------------- pyproject.toml | 2 +- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/poetry.lock b/poetry.lock index cd99825..6e64f5e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -621,6 +621,25 @@ files = [ {file = "frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817"}, ] +[[package]] +name = "id" +version = "1.5.0" +description = "A tool for generating OIDC identities" +optional = false +python-versions = ">=3.8" +files = [ + {file = "id-1.5.0-py3-none-any.whl", hash = "sha256:f1434e1cef91f2cbb8a4ec64663d5a23b9ed43ef44c4c957d02583d61714c658"}, + {file = "id-1.5.0.tar.gz", hash = "sha256:292cb8a49eacbbdbce97244f47a97b4c62540169c976552e497fd57df0734c1d"}, +] + +[package.dependencies] +requests = "*" + +[package.extras] +dev = ["build", "bump (>=1.3.2)", "id[lint,test]"] +lint = ["bandit", "interrogate", "mypy", "ruff (<0.8.2)", "types-requests"] +test = ["coverage[toml]", "pretend", "pytest", "pytest-cov"] + [[package]] name = "idna" version = "3.10" @@ -1012,20 +1031,6 @@ files = [ {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] -[[package]] -name = "pkginfo" -version = "1.12.0" -description = "Query metadata from sdists / bdists / installed packages." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pkginfo-1.12.0-py3-none-any.whl", hash = "sha256:dcd589c9be4da8973eceffa247733c144812759aa67eaf4bbf97016a02f39088"}, - {file = "pkginfo-1.12.0.tar.gz", hash = "sha256:8ad91a0445a036782b9366ef8b8c2c50291f83a553478ba8580c73d3215700cf"}, -] - -[package.extras] -testing = ["pytest", "pytest-cov", "wheel"] - [[package]] name = "platformdirs" version = "4.3.6" @@ -1466,20 +1471,20 @@ files = [ [[package]] name = "twine" -version = "6.0.1" +version = "6.1.0" description = "Collection of utilities for publishing packages on PyPI" optional = false python-versions = ">=3.8" files = [ - {file = "twine-6.0.1-py3-none-any.whl", hash = "sha256:9c6025b203b51521d53e200f4a08b116dee7500a38591668c6a6033117bdc218"}, - {file = "twine-6.0.1.tar.gz", hash = "sha256:36158b09df5406e1c9c1fb8edb24fc2be387709443e7376689b938531582ee27"}, + {file = "twine-6.1.0-py3-none-any.whl", hash = "sha256:a47f973caf122930bf0fbbf17f80b83bc1602c9ce393c7845f289a3001dc5384"}, + {file = "twine-6.1.0.tar.gz", hash = "sha256:be324f6272eff91d07ee93f251edf232fc647935dd585ac003539b42404a8dbd"}, ] [package.dependencies] +id = "*" importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""} keyring = {version = ">=15.1", markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\""} -packaging = "*" -pkginfo = ">=1.8.1" +packaging = ">=24.0" readme-renderer = ">=35.0" requests = ">=2.20" requests-toolbelt = ">=0.8.0,<0.9.0 || >0.9.0" @@ -1652,4 +1657,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9" -content-hash = "7bdbca53bb1649a03058590ca7d26086d9c1a224a23e87a68a7e570cde860283" +content-hash = "93103f6e2a0441ff5a0f006ab72d4608fa1955cf29f06e176686522d24a9571d" diff --git a/pyproject.toml b/pyproject.toml index 605d4af..7ee8b94 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,7 @@ polling2= "0.5.0" black= "24.10.0" flake8= "7.1.1" pytest = "~8.2.2" -twine = "~6.0.0" +twine = ">=6.0,<6.2" [tool.poetry.scripts] cloudfoundry-client = "cloudfoundry_client.main.main:main" From 773f5ee81815a8ed07368792ded74f8efa573413 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 08:33:57 +0100 Subject: [PATCH 191/264] Bump pytest from 8.2.2 to 8.3.4 (#230) Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.2.2 to 8.3.4. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/8.2.2...8.3.4) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 10 +++++----- pyproject.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/poetry.lock b/poetry.lock index 6e64f5e..7337377 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1233,13 +1233,13 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pytest" -version = "8.2.2" +version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"}, - {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"}, + {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, + {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, ] [package.dependencies] @@ -1247,7 +1247,7 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.5,<2.0" +pluggy = ">=1.5,<2" tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] @@ -1657,4 +1657,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9" -content-hash = "93103f6e2a0441ff5a0f006ab72d4608fa1955cf29f06e176686522d24a9571d" +content-hash = "d6a1a59ba13eef60454561c6b3cf44e33d9b8b1da82c5e72b0980845d6ac1fc2" diff --git a/pyproject.toml b/pyproject.toml index 7ee8b94..0b9bec7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ polling2= "0.5.0" [tool.poetry.group.dev.dependencies] black= "24.10.0" flake8= "7.1.1" -pytest = "~8.2.2" +pytest = ">=8.2.2,<8.4.0" twine = ">=6.0,<6.2" [tool.poetry.scripts] From dbe277a1cddb0c11fa71672a33ffa208a0138240 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 08:46:36 +0100 Subject: [PATCH 192/264] Bump black from 24.10.0 to 25.1.0 (#229) Bumps [black](https://github.com/psf/black) from 24.10.0 to 25.1.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/24.10.0...25.1.0) --- updated-dependencies: - dependency-name: black dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 48 ++++++++++++++++++++++++------------------------ pyproject.toml | 2 +- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/poetry.lock b/poetry.lock index 7337377..858af8a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -170,33 +170,33 @@ testing = ["jaraco.test", "pytest (!=8.0.*)", "pytest (>=6,!=8.1.*)", "pytest-ch [[package]] name = "black" -version = "24.10.0" +version = "25.1.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.9" files = [ - {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, - {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, - {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, - {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, - {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, - {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, - {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, - {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, - {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, - {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, - {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, - {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, - {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, - {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, - {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, - {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, - {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, - {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, - {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, - {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, - {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, - {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, + {file = "black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32"}, + {file = "black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da"}, + {file = "black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7"}, + {file = "black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9"}, + {file = "black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0"}, + {file = "black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299"}, + {file = "black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096"}, + {file = "black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2"}, + {file = "black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b"}, + {file = "black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc"}, + {file = "black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f"}, + {file = "black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba"}, + {file = "black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f"}, + {file = "black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3"}, + {file = "black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171"}, + {file = "black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18"}, + {file = "black-25.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1ee0a0c330f7b5130ce0caed9936a904793576ef4d2b98c40835d6a65afa6a0"}, + {file = "black-25.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3df5f1bf91d36002b0a75389ca8663510cf0531cca8aa5c1ef695b46d98655f"}, + {file = "black-25.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9e6827d563a2c820772b32ce8a42828dc6790f095f441beef18f96aa6f8294e"}, + {file = "black-25.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:bacabb307dca5ebaf9c118d2d2f6903da0d62c9faa82bd21a33eecc319559355"}, + {file = "black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717"}, + {file = "black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666"}, ] [package.dependencies] @@ -1657,4 +1657,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9" -content-hash = "d6a1a59ba13eef60454561c6b3cf44e33d9b8b1da82c5e72b0980845d6ac1fc2" +content-hash = "fa57666a20d0d60b34b8691a5c7efbf587cb9015cecfdb44fd88ce1278c56ec2" diff --git a/pyproject.toml b/pyproject.toml index 0b9bec7..f9832b9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ requests = ">=2.5.0" polling2= "0.5.0" [tool.poetry.group.dev.dependencies] -black= "24.10.0" +black= "25.1.0" flake8= "7.1.1" pytest = ">=8.2.2,<8.4.0" twine = ">=6.0,<6.2" From 6bbcfc2d858a93fc933f6bbe8e7ee26ef174ca6a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Apr 2025 10:14:26 +0200 Subject: [PATCH 193/264] Bump flake8 from 7.1.1 to 7.2.0 (#233) Bumps [flake8](https://github.com/pycqa/flake8) from 7.1.1 to 7.2.0. - [Commits](https://github.com/pycqa/flake8/compare/7.1.1...7.2.0) --- updated-dependencies: - dependency-name: flake8 dependency-version: 7.2.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 144 ++++++++++++++++++++++++++++++++++++++----------- pyproject.toml | 2 +- 2 files changed, 113 insertions(+), 33 deletions(-) diff --git a/poetry.lock b/poetry.lock index 858af8a..d63ce82 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -6,6 +6,7 @@ version = "2.4.4" description = "Happy Eyeballs for asyncio" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "aiohappyeyeballs-2.4.4-py3-none-any.whl", hash = "sha256:a980909d50efcd44795c4afeca523296716d50cd756ddca6af8c65b996e27de8"}, {file = "aiohappyeyeballs-2.4.4.tar.gz", hash = "sha256:5fdd7d87889c63183afc18ce9271f9b0a7d32c2303e394468dd45d514a757745"}, @@ -17,6 +18,7 @@ version = "3.11.11" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "aiohttp-3.11.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a60804bff28662cbcf340a4d61598891f12eea3a66af48ecfdc975ceec21e3c8"}, {file = "aiohttp-3.11.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b4fa1cb5f270fb3eab079536b764ad740bb749ce69a94d4ec30ceee1b5940d5"}, @@ -107,7 +109,7 @@ propcache = ">=0.2.0" yarl = ">=1.17.0,<2.0" [package.extras] -speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] +speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.2.0) ; sys_platform == \"linux\" or sys_platform == \"darwin\"", "brotlicffi ; platform_python_implementation != \"CPython\""] [[package]] name = "aiosignal" @@ -115,6 +117,7 @@ version = "1.3.2" description = "aiosignal: a list of registered asynchronous callbacks" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, @@ -129,6 +132,8 @@ version = "5.0.1" description = "Timeout context manager for asyncio programs" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version < \"3.11\"" files = [ {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, @@ -140,18 +145,19 @@ version = "24.3.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308"}, {file = "attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff"}, ] [package.extras] -benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] +tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] [[package]] name = "backports-tarfile" @@ -159,6 +165,8 @@ version = "1.2.0" description = "Backport of CPython tarfile module" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\"" files = [ {file = "backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34"}, {file = "backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991"}, @@ -174,6 +182,7 @@ version = "25.1.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32"}, {file = "black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da"}, @@ -220,6 +229,7 @@ version = "2024.12.14" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" +groups = ["main", "dev"] files = [ {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, @@ -231,6 +241,8 @@ version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\"" files = [ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, @@ -310,6 +322,7 @@ version = "3.4.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, @@ -411,6 +424,7 @@ version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -425,6 +439,8 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +markers = "platform_system == \"Windows\" or sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -436,6 +452,8 @@ version = "43.0.3" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" files = [ {file = "cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e"}, {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e"}, @@ -485,6 +503,7 @@ version = "0.21.2" description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, @@ -496,6 +515,8 @@ version = "1.2.2" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "python_version < \"3.11\"" files = [ {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, @@ -506,19 +527,20 @@ test = ["pytest (>=6)"] [[package]] name = "flake8" -version = "7.1.1" +version = "7.2.0" description = "the modular source code checker: pep8 pyflakes and co" optional = false -python-versions = ">=3.8.1" +python-versions = ">=3.9" +groups = ["dev"] files = [ - {file = "flake8-7.1.1-py2.py3-none-any.whl", hash = "sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213"}, - {file = "flake8-7.1.1.tar.gz", hash = "sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38"}, + {file = "flake8-7.2.0-py2.py3-none-any.whl", hash = "sha256:93b92ba5bdb60754a6da14fa3b93a9361fd00a59632ada61fd7b130436c40343"}, + {file = "flake8-7.2.0.tar.gz", hash = "sha256:fa558ae3f6f7dbf2b4f22663e5343b6b6023620461f8d4ff2019ef4b5ee70426"}, ] [package.dependencies] mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.12.0,<2.13.0" -pyflakes = ">=3.2.0,<3.3.0" +pycodestyle = ">=2.13.0,<2.14.0" +pyflakes = ">=3.3.0,<3.4.0" [[package]] name = "frozenlist" @@ -526,6 +548,7 @@ version = "1.5.0" description = "A list-like structure which implements collections.abc.MutableSequence" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a"}, {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb"}, @@ -627,6 +650,7 @@ version = "1.5.0" description = "A tool for generating OIDC identities" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "id-1.5.0-py3-none-any.whl", hash = "sha256:f1434e1cef91f2cbb8a4ec64663d5a23b9ed43ef44c4c957d02583d61714c658"}, {file = "id-1.5.0.tar.gz", hash = "sha256:292cb8a49eacbbdbce97244f47a97b4c62540169c976552e497fd57df0734c1d"}, @@ -646,6 +670,7 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" +groups = ["main", "dev"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -660,6 +685,8 @@ version = "8.5.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version < \"3.10\"" files = [ {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, @@ -669,12 +696,12 @@ files = [ zipp = ">=3.20" [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +test = ["flufl.flake8", "importlib-resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] [[package]] @@ -683,6 +710,7 @@ version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, @@ -694,6 +722,8 @@ version = "3.4.0" description = "Utility functions for Python class constructs" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" files = [ {file = "jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790"}, {file = "jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd"}, @@ -712,6 +742,8 @@ version = "6.0.1" description = "Useful decorators and context managers" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" files = [ {file = "jaraco.context-6.0.1-py3-none-any.whl", hash = "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4"}, {file = "jaraco_context-6.0.1.tar.gz", hash = "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3"}, @@ -722,7 +754,7 @@ files = [ [package.extras] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -test = ["portend", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"] +test = ["portend", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] [[package]] name = "jaraco-functools" @@ -730,6 +762,8 @@ version = "4.1.0" description = "Functools like those found in stdlib" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" files = [ {file = "jaraco.functools-4.1.0-py3-none-any.whl", hash = "sha256:ad159f13428bc4acbf5541ad6dec511f91573b90fba04df61dafa2a1231cf649"}, {file = "jaraco_functools-4.1.0.tar.gz", hash = "sha256:70f7e0e2ae076498e212562325e805204fc092d7b4c17e0e86c959e249701a9d"}, @@ -739,7 +773,7 @@ files = [ more-itertools = "*" [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] @@ -752,6 +786,8 @@ version = "0.8.0" description = "Low-level, pure Python DBus protocol wrapper." optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" files = [ {file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"}, {file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"}, @@ -759,7 +795,7 @@ files = [ [package.extras] test = ["async-timeout", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"] -trio = ["async_generator", "trio"] +trio = ["async_generator ; python_version == \"3.6\"", "trio"] [[package]] name = "keyring" @@ -767,6 +803,8 @@ version = "25.6.0" description = "Store and access your passwords safely." optional = false python-versions = ">=3.9" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" files = [ {file = "keyring-25.6.0-py3-none-any.whl", hash = "sha256:552a3f7af126ece7ed5c89753650eec89c7eaae8617d0aa4d9ad2b75111266bd"}, {file = "keyring-25.6.0.tar.gz", hash = "sha256:0b39998aa941431eb3d9b0d4b2460bc773b9df6fed7621c2dfb291a7e0187a66"}, @@ -782,7 +820,7 @@ pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""} SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] completion = ["shtab (>=1.1.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] @@ -796,6 +834,7 @@ version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, @@ -820,6 +859,7 @@ version = "0.7.0" description = "McCabe checker, plugin for flake8" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, @@ -831,6 +871,7 @@ version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, @@ -842,6 +883,8 @@ version = "10.5.0" description = "More routines for operating on iterables, beyond itertools" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" files = [ {file = "more-itertools-10.5.0.tar.gz", hash = "sha256:5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6"}, {file = "more_itertools-10.5.0-py3-none-any.whl", hash = "sha256:037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef"}, @@ -853,6 +896,7 @@ version = "6.1.0" description = "multidict implementation" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, @@ -957,6 +1001,7 @@ version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" +groups = ["dev"] files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, @@ -968,6 +1013,7 @@ version = "0.2.20" description = "Python binding to Ammonia HTML sanitizer Rust crate" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "nh3-0.2.20-cp313-cp313t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e1061a4ab6681f6bdf72b110eea0c4e1379d57c9de937db3be4202f7ad6043db"}, {file = "nh3-0.2.20-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb4254b1dac4a1ee49919a5b3f1caf9803ea8dada1816d9e8289e63d3cd0dd9a"}, @@ -1001,6 +1047,7 @@ version = "1.4.2" description = "A client library for OAuth2" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "oauth2-client-1.4.2.tar.gz", hash = "sha256:5381900448ff1ae762eb7c65c501002eac46bb5ca2f49477fdfeaf9e9969f284"}, {file = "oauth2_client-1.4.2-py3-none-any.whl", hash = "sha256:7b938ba8166128a3c4c15ad23ca0c95a2468f8e8b6069d019ebc73360c15c7ca"}, @@ -1015,6 +1062,7 @@ version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -1026,6 +1074,7 @@ version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -1037,6 +1086,7 @@ version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, @@ -1053,6 +1103,7 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -1068,6 +1119,7 @@ version = "0.5.0" description = "Updated polling utility with many configurable options" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "polling2-0.5.0-py2.py3-none-any.whl", hash = "sha256:ad86d56fbd7502f0856cac2d0109d595c18fa6c7fb12c88cee5e5d16c17286c1"}, {file = "polling2-0.5.0.tar.gz", hash = "sha256:90b7da82cf7adbb48029724d3546af93f21ab6e592ec37c8c4619aedd010e342"}, @@ -1079,6 +1131,7 @@ version = "0.2.1" description = "Accelerated property cache" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6"}, {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2"}, @@ -1170,6 +1223,7 @@ version = "5.29.2" description = "" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "protobuf-5.29.2-cp310-abi3-win32.whl", hash = "sha256:c12ba8249f5624300cf51c3d0bfe5be71a60c63e4dcf51ffe9a68771d958c851"}, {file = "protobuf-5.29.2-cp310-abi3-win_amd64.whl", hash = "sha256:842de6d9241134a973aab719ab42b008a18a90f9f07f06ba480df268f86432f9"}, @@ -1186,13 +1240,14 @@ files = [ [[package]] name = "pycodestyle" -version = "2.12.1" +version = "2.13.0" description = "Python style guide checker" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["dev"] files = [ - {file = "pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3"}, - {file = "pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"}, + {file = "pycodestyle-2.13.0-py2.py3-none-any.whl", hash = "sha256:35863c5974a271c7a726ed228a14a4f6daf49df369d8c50cd9a6f58a5e143ba9"}, + {file = "pycodestyle-2.13.0.tar.gz", hash = "sha256:c8415bf09abe81d9c7f872502a6eee881fbe85d8763dd5b9924bb0a01d67efae"}, ] [[package]] @@ -1201,6 +1256,8 @@ version = "2.22" description = "C parser in Python" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\"" files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, @@ -1208,13 +1265,14 @@ files = [ [[package]] name = "pyflakes" -version = "3.2.0" +version = "3.3.2" description = "passive checker of Python programs" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["dev"] files = [ - {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, - {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, + {file = "pyflakes-3.3.2-py2.py3-none-any.whl", hash = "sha256:5039c8339cbb1944045f4ee5466908906180f13cc99cc9949348d10f82a5c32a"}, + {file = "pyflakes-3.3.2.tar.gz", hash = "sha256:6dfd61d87b97fba5dcfaaf781171ac16be16453be6d816147989e7f6e6a9576b"}, ] [[package]] @@ -1223,6 +1281,7 @@ version = "2.19.0" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pygments-2.19.0-py3-none-any.whl", hash = "sha256:4755e6e64d22161d5b61432c0600c923c5927214e7c956e31c23923c89251a9b"}, {file = "pygments-2.19.0.tar.gz", hash = "sha256:afc4146269910d4bdfabcd27c24923137a74d562a23a320a41a55ad303e19783"}, @@ -1237,6 +1296,7 @@ version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, @@ -1259,6 +1319,8 @@ version = "0.2.3" description = "A (partial) reimplementation of pywin32 using ctypes/cffi" optional = false python-versions = ">=3.6" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"win32\"" files = [ {file = "pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755"}, {file = "pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8"}, @@ -1270,6 +1332,7 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -1332,6 +1395,7 @@ version = "44.0" description = "readme_renderer is a library for rendering readme descriptions for Warehouse" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "readme_renderer-44.0-py3-none-any.whl", hash = "sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151"}, {file = "readme_renderer-44.0.tar.gz", hash = "sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1"}, @@ -1351,6 +1415,7 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -1372,6 +1437,7 @@ version = "1.0.0" description = "A utility belt for advanced users of python-requests" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["dev"] files = [ {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, @@ -1386,6 +1452,7 @@ version = "2.0.0" description = "Validating URI References per RFC 3986" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd"}, {file = "rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c"}, @@ -1400,6 +1467,7 @@ version = "13.9.4" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" +groups = ["dev"] files = [ {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, @@ -1419,6 +1487,8 @@ version = "3.3.3" description = "Python bindings to FreeDesktop.org Secret Service API" optional = false python-versions = ">=3.6" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" files = [ {file = "SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"}, {file = "SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"}, @@ -1434,6 +1504,8 @@ version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version < \"3.11\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, @@ -1475,6 +1547,7 @@ version = "6.1.0" description = "Collection of utilities for publishing packages on PyPI" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "twine-6.1.0-py3-none-any.whl", hash = "sha256:a47f973caf122930bf0fbbf17f80b83bc1602c9ce393c7845f289a3001dc5384"}, {file = "twine-6.1.0.tar.gz", hash = "sha256:be324f6272eff91d07ee93f251edf232fc647935dd585ac003539b42404a8dbd"}, @@ -1501,6 +1574,8 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] +markers = "python_version < \"3.11\"" files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, @@ -1512,13 +1587,14 @@ version = "2.3.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" +groups = ["main", "dev"] files = [ {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -1529,6 +1605,7 @@ version = "1.8.0" description = "WebSocket client for Python with low level API options" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526"}, {file = "websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da"}, @@ -1545,6 +1622,7 @@ version = "1.18.3" description = "Yet another URL library" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"}, {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"}, @@ -1641,20 +1719,22 @@ version = "3.21.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version < \"3.10\"" files = [ {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = ">=3.9" -content-hash = "fa57666a20d0d60b34b8691a5c7efbf587cb9015cecfdb44fd88ce1278c56ec2" +content-hash = "70bbc48156b21af9642d899dbbe0425c5a4dea1e4942ca9308280ebf5512f7b4" diff --git a/pyproject.toml b/pyproject.toml index f9832b9..f066f59 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,7 @@ polling2= "0.5.0" [tool.poetry.group.dev.dependencies] black= "25.1.0" -flake8= "7.1.1" +flake8= "7.2.0" pytest = ">=8.2.2,<8.4.0" twine = ">=6.0,<6.2" From 49fb8efd12cdda1f9dd6495ebc426a7206a57424 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Apr 2025 13:41:45 +0200 Subject: [PATCH 194/264] Bump pytest from 8.3.4 to 8.3.5 (#234) Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.3.4 to 8.3.5. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/8.3.4...8.3.5) --- updated-dependencies: - dependency-name: pytest dependency-version: 8.3.5 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index d63ce82..7649095 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1292,14 +1292,14 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pytest" -version = "8.3.4" +version = "8.3.5" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, - {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, + {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, + {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, ] [package.dependencies] From f63d51cb1c928c696315670f7cd3e2dd1f8ddc3d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Apr 2025 13:45:28 +0200 Subject: [PATCH 195/264] Bump aiohttp from 3.11.11 to 3.11.15 (#235) Bumps [aiohttp](https://github.com/aio-libs/aiohttp) from 3.11.11 to 3.11.15. - [Release notes](https://github.com/aio-libs/aiohttp/releases) - [Changelog](https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst) - [Commits](https://github.com/aio-libs/aiohttp/compare/v3.11.11...v3.11.15) --- updated-dependencies: - dependency-name: aiohttp dependency-version: 3.11.15 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 159 +++++++++++++++++++++++++++------------------------- 1 file changed, 82 insertions(+), 77 deletions(-) diff --git a/poetry.lock b/poetry.lock index 7649095..d894530 100644 --- a/poetry.lock +++ b/poetry.lock @@ -14,88 +14,93 @@ files = [ [[package]] name = "aiohttp" -version = "3.11.11" +version = "3.11.16" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "aiohttp-3.11.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a60804bff28662cbcf340a4d61598891f12eea3a66af48ecfdc975ceec21e3c8"}, - {file = "aiohttp-3.11.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b4fa1cb5f270fb3eab079536b764ad740bb749ce69a94d4ec30ceee1b5940d5"}, - {file = "aiohttp-3.11.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:731468f555656767cda219ab42e033355fe48c85fbe3ba83a349631541715ba2"}, - {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb23d8bb86282b342481cad4370ea0853a39e4a32a0042bb52ca6bdde132df43"}, - {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f047569d655f81cb70ea5be942ee5d4421b6219c3f05d131f64088c73bb0917f"}, - {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd7659baae9ccf94ae5fe8bfaa2c7bc2e94d24611528395ce88d009107e00c6d"}, - {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af01e42ad87ae24932138f154105e88da13ce7d202a6de93fafdafb2883a00ef"}, - {file = "aiohttp-3.11.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5854be2f3e5a729800bac57a8d76af464e160f19676ab6aea74bde18ad19d438"}, - {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6526e5fb4e14f4bbf30411216780c9967c20c5a55f2f51d3abd6de68320cc2f3"}, - {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:85992ee30a31835fc482468637b3e5bd085fa8fe9392ba0bdcbdc1ef5e9e3c55"}, - {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:88a12ad8ccf325a8a5ed80e6d7c3bdc247d66175afedbe104ee2aaca72960d8e"}, - {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0a6d3fbf2232e3a08c41eca81ae4f1dff3d8f1a30bae415ebe0af2d2458b8a33"}, - {file = "aiohttp-3.11.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:84a585799c58b795573c7fa9b84c455adf3e1d72f19a2bf498b54a95ae0d194c"}, - {file = "aiohttp-3.11.11-cp310-cp310-win32.whl", hash = "sha256:bfde76a8f430cf5c5584553adf9926534352251d379dcb266ad2b93c54a29745"}, - {file = "aiohttp-3.11.11-cp310-cp310-win_amd64.whl", hash = "sha256:0fd82b8e9c383af11d2b26f27a478640b6b83d669440c0a71481f7c865a51da9"}, - {file = "aiohttp-3.11.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ba74ec819177af1ef7f59063c6d35a214a8fde6f987f7661f4f0eecc468a8f76"}, - {file = "aiohttp-3.11.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4af57160800b7a815f3fe0eba9b46bf28aafc195555f1824555fa2cfab6c1538"}, - {file = "aiohttp-3.11.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffa336210cf9cd8ed117011085817d00abe4c08f99968deef0013ea283547204"}, - {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81b8fe282183e4a3c7a1b72f5ade1094ed1c6345a8f153506d114af5bf8accd9"}, - {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3af41686ccec6a0f2bdc66686dc0f403c41ac2089f80e2214a0f82d001052c03"}, - {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70d1f9dde0e5dd9e292a6d4d00058737052b01f3532f69c0c65818dac26dc287"}, - {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:249cc6912405917344192b9f9ea5cd5b139d49e0d2f5c7f70bdfaf6b4dbf3a2e"}, - {file = "aiohttp-3.11.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0eb98d90b6690827dcc84c246811feeb4e1eea683c0eac6caed7549be9c84665"}, - {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec82bf1fda6cecce7f7b915f9196601a1bd1a3079796b76d16ae4cce6d0ef89b"}, - {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9fd46ce0845cfe28f108888b3ab17abff84ff695e01e73657eec3f96d72eef34"}, - {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bd176afcf8f5d2aed50c3647d4925d0db0579d96f75a31e77cbaf67d8a87742d"}, - {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ec2aa89305006fba9ffb98970db6c8221541be7bee4c1d027421d6f6df7d1ce2"}, - {file = "aiohttp-3.11.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:92cde43018a2e17d48bb09c79e4d4cb0e236de5063ce897a5e40ac7cb4878773"}, - {file = "aiohttp-3.11.11-cp311-cp311-win32.whl", hash = "sha256:aba807f9569455cba566882c8938f1a549f205ee43c27b126e5450dc9f83cc62"}, - {file = "aiohttp-3.11.11-cp311-cp311-win_amd64.whl", hash = "sha256:ae545f31489548c87b0cced5755cfe5a5308d00407000e72c4fa30b19c3220ac"}, - {file = "aiohttp-3.11.11-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e595c591a48bbc295ebf47cb91aebf9bd32f3ff76749ecf282ea7f9f6bb73886"}, - {file = "aiohttp-3.11.11-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3ea1b59dc06396b0b424740a10a0a63974c725b1c64736ff788a3689d36c02d2"}, - {file = "aiohttp-3.11.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8811f3f098a78ffa16e0ea36dffd577eb031aea797cbdba81be039a4169e242c"}, - {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7227b87a355ce1f4bf83bfae4399b1f5bb42e0259cb9405824bd03d2f4336a"}, - {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d40f9da8cabbf295d3a9dae1295c69975b86d941bc20f0a087f0477fa0a66231"}, - {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffb3dc385f6bb1568aa974fe65da84723210e5d9707e360e9ecb51f59406cd2e"}, - {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8f5f7515f3552d899c61202d99dcb17d6e3b0de777900405611cd747cecd1b8"}, - {file = "aiohttp-3.11.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3499c7ffbfd9c6a3d8d6a2b01c26639da7e43d47c7b4f788016226b1e711caa8"}, - {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8e2bf8029dbf0810c7bfbc3e594b51c4cc9101fbffb583a3923aea184724203c"}, - {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b6212a60e5c482ef90f2d788835387070a88d52cf6241d3916733c9176d39eab"}, - {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d119fafe7b634dbfa25a8c597718e69a930e4847f0b88e172744be24515140da"}, - {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:6fba278063559acc730abf49845d0e9a9e1ba74f85f0ee6efd5803f08b285853"}, - {file = "aiohttp-3.11.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:92fc484e34b733704ad77210c7957679c5c3877bd1e6b6d74b185e9320cc716e"}, - {file = "aiohttp-3.11.11-cp312-cp312-win32.whl", hash = "sha256:9f5b3c1ed63c8fa937a920b6c1bec78b74ee09593b3f5b979ab2ae5ef60d7600"}, - {file = "aiohttp-3.11.11-cp312-cp312-win_amd64.whl", hash = "sha256:1e69966ea6ef0c14ee53ef7a3d68b564cc408121ea56c0caa2dc918c1b2f553d"}, - {file = "aiohttp-3.11.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:541d823548ab69d13d23730a06f97460f4238ad2e5ed966aaf850d7c369782d9"}, - {file = "aiohttp-3.11.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:929f3ed33743a49ab127c58c3e0a827de0664bfcda566108989a14068f820194"}, - {file = "aiohttp-3.11.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0882c2820fd0132240edbb4a51eb8ceb6eef8181db9ad5291ab3332e0d71df5f"}, - {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b63de12e44935d5aca7ed7ed98a255a11e5cb47f83a9fded7a5e41c40277d104"}, - {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa54f8ef31d23c506910c21163f22b124facb573bff73930735cf9fe38bf7dff"}, - {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a344d5dc18074e3872777b62f5f7d584ae4344cd6006c17ba12103759d407af3"}, - {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7fb429ab1aafa1f48578eb315ca45bd46e9c37de11fe45c7f5f4138091e2f1"}, - {file = "aiohttp-3.11.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c341c7d868750e31961d6d8e60ff040fb9d3d3a46d77fd85e1ab8e76c3e9a5c4"}, - {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ed9ee95614a71e87f1a70bc81603f6c6760128b140bc4030abe6abaa988f1c3d"}, - {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:de8d38f1c2810fa2a4f1d995a2e9c70bb8737b18da04ac2afbf3971f65781d87"}, - {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a9b7371665d4f00deb8f32208c7c5e652059b0fda41cf6dbcac6114a041f1cc2"}, - {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:620598717fce1b3bd14dd09947ea53e1ad510317c85dda2c9c65b622edc96b12"}, - {file = "aiohttp-3.11.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf8d9bfee991d8acc72d060d53860f356e07a50f0e0d09a8dfedea1c554dd0d5"}, - {file = "aiohttp-3.11.11-cp313-cp313-win32.whl", hash = "sha256:9d73ee3725b7a737ad86c2eac5c57a4a97793d9f442599bea5ec67ac9f4bdc3d"}, - {file = "aiohttp-3.11.11-cp313-cp313-win_amd64.whl", hash = "sha256:c7a06301c2fb096bdb0bd25fe2011531c1453b9f2c163c8031600ec73af1cc99"}, - {file = "aiohttp-3.11.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3e23419d832d969f659c208557de4a123e30a10d26e1e14b73431d3c13444c2e"}, - {file = "aiohttp-3.11.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:21fef42317cf02e05d3b09c028712e1d73a9606f02467fd803f7c1f39cc59add"}, - {file = "aiohttp-3.11.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1f21bb8d0235fc10c09ce1d11ffbd40fc50d3f08a89e4cf3a0c503dc2562247a"}, - {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1642eceeaa5ab6c9b6dfeaaa626ae314d808188ab23ae196a34c9d97efb68350"}, - {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2170816e34e10f2fd120f603e951630f8a112e1be3b60963a1f159f5699059a6"}, - {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8be8508d110d93061197fd2d6a74f7401f73b6d12f8822bbcd6d74f2b55d71b1"}, - {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4eed954b161e6b9b65f6be446ed448ed3921763cc432053ceb606f89d793927e"}, - {file = "aiohttp-3.11.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6c9af134da4bc9b3bd3e6a70072509f295d10ee60c697826225b60b9959acdd"}, - {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:44167fc6a763d534a6908bdb2592269b4bf30a03239bcb1654781adf5e49caf1"}, - {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:479b8c6ebd12aedfe64563b85920525d05d394b85f166b7873c8bde6da612f9c"}, - {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:10b4ff0ad793d98605958089fabfa350e8e62bd5d40aa65cdc69d6785859f94e"}, - {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:b540bd67cfb54e6f0865ceccd9979687210d7ed1a1cc8c01f8e67e2f1e883d28"}, - {file = "aiohttp-3.11.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1dac54e8ce2ed83b1f6b1a54005c87dfed139cf3f777fdc8afc76e7841101226"}, - {file = "aiohttp-3.11.11-cp39-cp39-win32.whl", hash = "sha256:568c1236b2fde93b7720f95a890741854c1200fba4a3471ff48b2934d2d93fd3"}, - {file = "aiohttp-3.11.11-cp39-cp39-win_amd64.whl", hash = "sha256:943a8b052e54dfd6439fd7989f67fc6a7f2138d0a2cf0a7de5f18aa4fe7eb3b1"}, - {file = "aiohttp-3.11.11.tar.gz", hash = "sha256:bb49c7f1e6ebf3821a42d81d494f538107610c3a705987f53068546b0e90303e"}, + {file = "aiohttp-3.11.16-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb46bb0f24813e6cede6cc07b1961d4b04f331f7112a23b5e21f567da4ee50aa"}, + {file = "aiohttp-3.11.16-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:54eb3aead72a5c19fad07219acd882c1643a1027fbcdefac9b502c267242f955"}, + {file = "aiohttp-3.11.16-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:38bea84ee4fe24ebcc8edeb7b54bf20f06fd53ce4d2cc8b74344c5b9620597fd"}, + {file = "aiohttp-3.11.16-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0666afbe984f6933fe72cd1f1c3560d8c55880a0bdd728ad774006eb4241ecd"}, + {file = "aiohttp-3.11.16-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ba92a2d9ace559a0a14b03d87f47e021e4fa7681dc6970ebbc7b447c7d4b7cd"}, + {file = "aiohttp-3.11.16-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ad1d59fd7114e6a08c4814983bb498f391c699f3c78712770077518cae63ff7"}, + {file = "aiohttp-3.11.16-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b88a2bf26965f2015a771381624dd4b0839034b70d406dc74fd8be4cc053e3"}, + {file = "aiohttp-3.11.16-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:576f5ca28d1b3276026f7df3ec841ae460e0fc3aac2a47cbf72eabcfc0f102e1"}, + {file = "aiohttp-3.11.16-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a2a450bcce4931b295fc0848f384834c3f9b00edfc2150baafb4488c27953de6"}, + {file = "aiohttp-3.11.16-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:37dcee4906454ae377be5937ab2a66a9a88377b11dd7c072df7a7c142b63c37c"}, + {file = "aiohttp-3.11.16-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4d0c970c0d602b1017e2067ff3b7dac41c98fef4f7472ec2ea26fd8a4e8c2149"}, + {file = "aiohttp-3.11.16-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:004511d3413737700835e949433536a2fe95a7d0297edd911a1e9705c5b5ea43"}, + {file = "aiohttp-3.11.16-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:c15b2271c44da77ee9d822552201180779e5e942f3a71fb74e026bf6172ff287"}, + {file = "aiohttp-3.11.16-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ad9509ffb2396483ceacb1eee9134724443ee45b92141105a4645857244aecc8"}, + {file = "aiohttp-3.11.16-cp310-cp310-win32.whl", hash = "sha256:634d96869be6c4dc232fc503e03e40c42d32cfaa51712aee181e922e61d74814"}, + {file = "aiohttp-3.11.16-cp310-cp310-win_amd64.whl", hash = "sha256:938f756c2b9374bbcc262a37eea521d8a0e6458162f2a9c26329cc87fdf06534"}, + {file = "aiohttp-3.11.16-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8cb0688a8d81c63d716e867d59a9ccc389e97ac7037ebef904c2b89334407180"}, + {file = "aiohttp-3.11.16-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ad1fb47da60ae1ddfb316f0ff16d1f3b8e844d1a1e154641928ea0583d486ed"}, + {file = "aiohttp-3.11.16-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:df7db76400bf46ec6a0a73192b14c8295bdb9812053f4fe53f4e789f3ea66bbb"}, + {file = "aiohttp-3.11.16-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc3a145479a76ad0ed646434d09216d33d08eef0d8c9a11f5ae5cdc37caa3540"}, + {file = "aiohttp-3.11.16-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d007aa39a52d62373bd23428ba4a2546eed0e7643d7bf2e41ddcefd54519842c"}, + {file = "aiohttp-3.11.16-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6ddd90d9fb4b501c97a4458f1c1720e42432c26cb76d28177c5b5ad4e332601"}, + {file = "aiohttp-3.11.16-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a2f451849e6b39e5c226803dcacfa9c7133e9825dcefd2f4e837a2ec5a3bb98"}, + {file = "aiohttp-3.11.16-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8df6612df74409080575dca38a5237282865408016e65636a76a2eb9348c2567"}, + {file = "aiohttp-3.11.16-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:78e6e23b954644737e385befa0deb20233e2dfddf95dd11e9db752bdd2a294d3"}, + {file = "aiohttp-3.11.16-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:696ef00e8a1f0cec5e30640e64eca75d8e777933d1438f4facc9c0cdf288a810"}, + {file = "aiohttp-3.11.16-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e3538bc9fe1b902bef51372462e3d7c96fce2b566642512138a480b7adc9d508"}, + {file = "aiohttp-3.11.16-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3ab3367bb7f61ad18793fea2ef71f2d181c528c87948638366bf1de26e239183"}, + {file = "aiohttp-3.11.16-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:56a3443aca82abda0e07be2e1ecb76a050714faf2be84256dae291182ba59049"}, + {file = "aiohttp-3.11.16-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:61c721764e41af907c9d16b6daa05a458f066015abd35923051be8705108ed17"}, + {file = "aiohttp-3.11.16-cp311-cp311-win32.whl", hash = "sha256:3e061b09f6fa42997cf627307f220315e313ece74907d35776ec4373ed718b86"}, + {file = "aiohttp-3.11.16-cp311-cp311-win_amd64.whl", hash = "sha256:745f1ed5e2c687baefc3c5e7b4304e91bf3e2f32834d07baaee243e349624b24"}, + {file = "aiohttp-3.11.16-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:911a6e91d08bb2c72938bc17f0a2d97864c531536b7832abee6429d5296e5b27"}, + {file = "aiohttp-3.11.16-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac13b71761e49d5f9e4d05d33683bbafef753e876e8e5a7ef26e937dd766713"}, + {file = "aiohttp-3.11.16-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fd36c119c5d6551bce374fcb5c19269638f8d09862445f85a5a48596fd59f4bb"}, + {file = "aiohttp-3.11.16-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d489d9778522fbd0f8d6a5c6e48e3514f11be81cb0a5954bdda06f7e1594b321"}, + {file = "aiohttp-3.11.16-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69a2cbd61788d26f8f1e626e188044834f37f6ae3f937bd9f08b65fc9d7e514e"}, + {file = "aiohttp-3.11.16-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd464ba806e27ee24a91362ba3621bfc39dbbb8b79f2e1340201615197370f7c"}, + {file = "aiohttp-3.11.16-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce63ae04719513dd2651202352a2beb9f67f55cb8490c40f056cea3c5c355ce"}, + {file = "aiohttp-3.11.16-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09b00dd520d88eac9d1768439a59ab3d145065c91a8fab97f900d1b5f802895e"}, + {file = "aiohttp-3.11.16-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7f6428fee52d2bcf96a8aa7b62095b190ee341ab0e6b1bcf50c615d7966fd45b"}, + {file = "aiohttp-3.11.16-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:13ceac2c5cdcc3f64b9015710221ddf81c900c5febc505dbd8f810e770011540"}, + {file = "aiohttp-3.11.16-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fadbb8f1d4140825069db3fedbbb843290fd5f5bc0a5dbd7eaf81d91bf1b003b"}, + {file = "aiohttp-3.11.16-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6a792ce34b999fbe04a7a71a90c74f10c57ae4c51f65461a411faa70e154154e"}, + {file = "aiohttp-3.11.16-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f4065145bf69de124accdd17ea5f4dc770da0a6a6e440c53f6e0a8c27b3e635c"}, + {file = "aiohttp-3.11.16-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa73e8c2656a3653ae6c307b3f4e878a21f87859a9afab228280ddccd7369d71"}, + {file = "aiohttp-3.11.16-cp312-cp312-win32.whl", hash = "sha256:f244b8e541f414664889e2c87cac11a07b918cb4b540c36f7ada7bfa76571ea2"}, + {file = "aiohttp-3.11.16-cp312-cp312-win_amd64.whl", hash = "sha256:23a15727fbfccab973343b6d1b7181bfb0b4aa7ae280f36fd2f90f5476805682"}, + {file = "aiohttp-3.11.16-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a3814760a1a700f3cfd2f977249f1032301d0a12c92aba74605cfa6ce9f78489"}, + {file = "aiohttp-3.11.16-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9b751a6306f330801665ae69270a8a3993654a85569b3469662efaad6cf5cc50"}, + {file = "aiohttp-3.11.16-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ad497f38a0d6c329cb621774788583ee12321863cd4bd9feee1effd60f2ad133"}, + {file = "aiohttp-3.11.16-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca37057625693d097543bd88076ceebeb248291df9d6ca8481349efc0b05dcd0"}, + {file = "aiohttp-3.11.16-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5abcbba9f4b463a45c8ca8b7720891200658f6f46894f79517e6cd11f3405ca"}, + {file = "aiohttp-3.11.16-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f420bfe862fb357a6d76f2065447ef6f484bc489292ac91e29bc65d2d7a2c84d"}, + {file = "aiohttp-3.11.16-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58ede86453a6cf2d6ce40ef0ca15481677a66950e73b0a788917916f7e35a0bb"}, + {file = "aiohttp-3.11.16-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fdec0213244c39973674ca2a7f5435bf74369e7d4e104d6c7473c81c9bcc8c4"}, + {file = "aiohttp-3.11.16-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:72b1b03fb4655c1960403c131740755ec19c5898c82abd3961c364c2afd59fe7"}, + {file = "aiohttp-3.11.16-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:780df0d837276276226a1ff803f8d0fa5f8996c479aeef52eb040179f3156cbd"}, + {file = "aiohttp-3.11.16-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ecdb8173e6c7aa09eee342ac62e193e6904923bd232e76b4157ac0bfa670609f"}, + {file = "aiohttp-3.11.16-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a6db7458ab89c7d80bc1f4e930cc9df6edee2200127cfa6f6e080cf619eddfbd"}, + {file = "aiohttp-3.11.16-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2540ddc83cc724b13d1838026f6a5ad178510953302a49e6d647f6e1de82bc34"}, + {file = "aiohttp-3.11.16-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3b4e6db8dc4879015b9955778cfb9881897339c8fab7b3676f8433f849425913"}, + {file = "aiohttp-3.11.16-cp313-cp313-win32.whl", hash = "sha256:493910ceb2764f792db4dc6e8e4b375dae1b08f72e18e8f10f18b34ca17d0979"}, + {file = "aiohttp-3.11.16-cp313-cp313-win_amd64.whl", hash = "sha256:42864e70a248f5f6a49fdaf417d9bc62d6e4d8ee9695b24c5916cb4bb666c802"}, + {file = "aiohttp-3.11.16-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bbcba75fe879ad6fd2e0d6a8d937f34a571f116a0e4db37df8079e738ea95c71"}, + {file = "aiohttp-3.11.16-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:87a6e922b2b2401e0b0cf6b976b97f11ec7f136bfed445e16384fbf6fd5e8602"}, + {file = "aiohttp-3.11.16-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccf10f16ab498d20e28bc2b5c1306e9c1512f2840f7b6a67000a517a4b37d5ee"}, + {file = "aiohttp-3.11.16-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb3d0cc5cdb926090748ea60172fa8a213cec728bd6c54eae18b96040fcd6227"}, + {file = "aiohttp-3.11.16-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d07502cc14ecd64f52b2a74ebbc106893d9a9717120057ea9ea1fd6568a747e7"}, + {file = "aiohttp-3.11.16-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:776c8e959a01e5e8321f1dec77964cb6101020a69d5a94cd3d34db6d555e01f7"}, + {file = "aiohttp-3.11.16-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0902e887b0e1d50424112f200eb9ae3dfed6c0d0a19fc60f633ae5a57c809656"}, + {file = "aiohttp-3.11.16-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e87fd812899aa78252866ae03a048e77bd11b80fb4878ce27c23cade239b42b2"}, + {file = "aiohttp-3.11.16-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0a950c2eb8ff17361abd8c85987fd6076d9f47d040ebffce67dce4993285e973"}, + {file = "aiohttp-3.11.16-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:c10d85e81d0b9ef87970ecbdbfaeec14a361a7fa947118817fcea8e45335fa46"}, + {file = "aiohttp-3.11.16-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:7951decace76a9271a1ef181b04aa77d3cc309a02a51d73826039003210bdc86"}, + {file = "aiohttp-3.11.16-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14461157d8426bcb40bd94deb0450a6fa16f05129f7da546090cebf8f3123b0f"}, + {file = "aiohttp-3.11.16-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9756d9b9d4547e091f99d554fbba0d2a920aab98caa82a8fb3d3d9bee3c9ae85"}, + {file = "aiohttp-3.11.16-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:87944bd16b7fe6160607f6a17808abd25f17f61ae1e26c47a491b970fb66d8cb"}, + {file = "aiohttp-3.11.16-cp39-cp39-win32.whl", hash = "sha256:92b7ee222e2b903e0a4b329a9943d432b3767f2d5029dbe4ca59fb75223bbe2e"}, + {file = "aiohttp-3.11.16-cp39-cp39-win_amd64.whl", hash = "sha256:17ae4664031aadfbcb34fd40ffd90976671fa0c0286e6c4113989f78bebab37a"}, + {file = "aiohttp-3.11.16.tar.gz", hash = "sha256:16f8a2c9538c14a557b4d309ed4d0a7c60f0253e8ed7b6c9a2859a7582f8b1b8"}, ] [package.dependencies] From 043a65b74a520913d9cd11719ba4d6c0f65acda5 Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Sun, 18 May 2025 13:31:55 +0200 Subject: [PATCH 196/264] chore(logs) upgrade proto files and regenerate stubs (#238) --- cloudfoundry_client/doppler/client.py | 2 +- cloudfoundry_client/dropsonde/__init__.py | 3 - cloudfoundry_client/dropsonde/envelope_pb2.py | 18 +- cloudfoundry_client/dropsonde/error_pb2.py | 4 +- cloudfoundry_client/dropsonde/http_pb2.py | 16 +- cloudfoundry_client/dropsonde/log_pb2.py | 4 +- cloudfoundry_client/dropsonde/metric_pb2.py | 4 +- cloudfoundry_client/dropsonde/uuid_pb2.py | 4 +- poetry.lock | 1435 ++++++++--------- pyproject.toml | 2 +- tests/{v2 => }/test_doppler.py | 0 vendors/dropsonde-protocol/envelope.proto | 49 +- vendors/dropsonde-protocol/error.proto | 18 +- vendors/dropsonde-protocol/http.proto | 75 +- vendors/dropsonde-protocol/log.proto | 31 +- vendors/dropsonde-protocol/metric.proto | 68 +- vendors/dropsonde-protocol/uuid.proto | 9 +- 17 files changed, 917 insertions(+), 825 deletions(-) delete mode 100644 cloudfoundry_client/dropsonde/__init__.py rename tests/{v2 => }/test_doppler.py (100%) diff --git a/cloudfoundry_client/doppler/client.py b/cloudfoundry_client/doppler/client.py index ae16827..d79e869 100644 --- a/cloudfoundry_client/doppler/client.py +++ b/cloudfoundry_client/doppler/client.py @@ -16,7 +16,7 @@ class DopplerClient(object): - def __init__(self, doppler_endpoint: str, proxy: dict, verify_ssl: bool, credentials_manager: CredentialManager): + def __init__(self, doppler_endpoint: str, proxy: str, verify_ssl: bool, credentials_manager: CredentialManager): self.proxy_host = None self.proxy_port = None self.ws_doppler_endpoint = doppler_endpoint diff --git a/cloudfoundry_client/dropsonde/__init__.py b/cloudfoundry_client/dropsonde/__init__.py deleted file mode 100644 index ef603c2..0000000 --- a/cloudfoundry_client/dropsonde/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -import sys,os -# Ugly Hack needed see https://github.com/protocolbuffers/protobuf/issues/1491 -sys.path.append(os.path.dirname(__file__)) \ No newline at end of file diff --git a/cloudfoundry_client/dropsonde/envelope_pb2.py b/cloudfoundry_client/dropsonde/envelope_pb2.py index f4c1211..5dcb8f4 100644 --- a/cloudfoundry_client/dropsonde/envelope_pb2.py +++ b/cloudfoundry_client/dropsonde/envelope_pb2.py @@ -11,26 +11,26 @@ _sym_db = _symbol_database.Default() -import http_pb2 as http__pb2 -import log_pb2 as log__pb2 -import metric_pb2 as metric__pb2 -import error_pb2 as error__pb2 +import cloudfoundry_client.dropsonde.http_pb2 as http__pb2 +import cloudfoundry_client.dropsonde.log_pb2 as log__pb2 +import cloudfoundry_client.dropsonde.metric_pb2 as metric__pb2 +import cloudfoundry_client.dropsonde.error_pb2 as error__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0e\x65nvelope.proto\x12\x06\x65vents\x1a\nhttp.proto\x1a\tlog.proto\x1a\x0cmetric.proto\x1a\x0b\x65rror.proto\"\xde\x04\n\x08\x45nvelope\x12\x0e\n\x06origin\x18\x01 \x02(\t\x12-\n\teventType\x18\x02 \x02(\x0e\x32\x1a.events.Envelope.EventType\x12\x11\n\ttimestamp\x18\x06 \x01(\x03\x12\x12\n\ndeployment\x18\r \x01(\t\x12\x0b\n\x03job\x18\x0e \x01(\t\x12\r\n\x05index\x18\x0f \x01(\t\x12\n\n\x02ip\x18\x10 \x01(\t\x12(\n\x04tags\x18\x11 \x03(\x0b\x32\x1a.events.Envelope.TagsEntry\x12,\n\rhttpStartStop\x18\x07 \x01(\x0b\x32\x15.events.HttpStartStop\x12&\n\nlogMessage\x18\x08 \x01(\x0b\x32\x12.events.LogMessage\x12(\n\x0bvalueMetric\x18\t \x01(\x0b\x32\x13.events.ValueMetric\x12*\n\x0c\x63ounterEvent\x18\n \x01(\x0b\x32\x14.events.CounterEvent\x12\x1c\n\x05\x65rror\x18\x0b \x01(\x0b\x32\r.events.Error\x12\x30\n\x0f\x63ontainerMetric\x18\x0c \x01(\x0b\x32\x17.events.ContainerMetric\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"q\n\tEventType\x12\x11\n\rHttpStartStop\x10\x04\x12\x0e\n\nLogMessage\x10\x05\x12\x0f\n\x0bValueMetric\x10\x06\x12\x10\n\x0c\x43ounterEvent\x10\x07\x12\t\n\x05\x45rror\x10\x08\x12\x13\n\x0f\x43ontainerMetric\x10\tB1\n!org.cloudfoundry.dropsonde.eventsB\x0c\x45ventFactory') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0e\x65nvelope.proto\x12\x06\x65vents\x1a\nhttp.proto\x1a\tlog.proto\x1a\x0cmetric.proto\x1a\x0b\x65rror.proto\"\xab\x05\n\x08\x45nvelope\x12\x0e\n\x06origin\x18\x01 \x02(\t\x12-\n\teventType\x18\x02 \x02(\x0e\x32\x1a.events.Envelope.EventType\x12\x11\n\ttimestamp\x18\x06 \x01(\x03\x12\x12\n\ndeployment\x18\r \x01(\t\x12\x0b\n\x03job\x18\x0e \x01(\t\x12\r\n\x05index\x18\x0f \x01(\t\x12\n\n\x02ip\x18\x10 \x01(\t\x12(\n\x04tags\x18\x11 \x03(\x0b\x32\x1a.events.Envelope.TagsEntry\x12,\n\rhttpStartStop\x18\x07 \x01(\x0b\x32\x15.events.HttpStartStop\x12&\n\nlogMessage\x18\x08 \x01(\x0b\x32\x12.events.LogMessage\x12(\n\x0bvalueMetric\x18\t \x01(\x0b\x32\x13.events.ValueMetric\x12*\n\x0c\x63ounterEvent\x18\n \x01(\x0b\x32\x14.events.CounterEvent\x12\x1c\n\x05\x65rror\x18\x0b \x01(\x0b\x32\r.events.Error\x12\x30\n\x0f\x63ontainerMetric\x18\x0c \x01(\x0b\x32\x17.events.ContainerMetric\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x97\x01\n\tEventType\x12\x11\n\rHttpStartStop\x10\x04\x12\x0e\n\nLogMessage\x10\x05\x12\x0f\n\x0bValueMetric\x10\x06\x12\x10\n\x0c\x43ounterEvent\x10\x07\x12\t\n\x05\x45rror\x10\x08\x12\x13\n\x0f\x43ontainerMetric\x10\t\"\x04\x08\x01\x10\x03*\tHeartbeat*\tHttpStart*\x08HttpStopJ\x04\x08\x03\x10\x06R\tHeartbeatR\tHttpStartR\x08HttpStopBZ\n!org.cloudfoundry.dropsonde.eventsB\x0c\x45ventFactoryZ\'github.com/cloudfoundry/sonde-go/events') _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'envelope_pb2', globals()) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n!org.cloudfoundry.dropsonde.eventsB\014EventFactory' + DESCRIPTOR._serialized_options = b'\n!org.cloudfoundry.dropsonde.eventsB\014EventFactoryZ\'github.com/cloudfoundry/sonde-go/events' _ENVELOPE_TAGSENTRY._options = None _ENVELOPE_TAGSENTRY._serialized_options = b'8\001' _ENVELOPE._serialized_start=77 - _ENVELOPE._serialized_end=683 + _ENVELOPE._serialized_end=760 _ENVELOPE_TAGSENTRY._serialized_start=525 _ENVELOPE_TAGSENTRY._serialized_end=568 - _ENVELOPE_EVENTTYPE._serialized_start=570 - _ENVELOPE_EVENTTYPE._serialized_end=683 + _ENVELOPE_EVENTTYPE._serialized_start=571 + _ENVELOPE_EVENTTYPE._serialized_end=722 # @@protoc_insertion_point(module_scope) diff --git a/cloudfoundry_client/dropsonde/error_pb2.py b/cloudfoundry_client/dropsonde/error_pb2.py index 68e59ff..b4ccaa6 100644 --- a/cloudfoundry_client/dropsonde/error_pb2.py +++ b/cloudfoundry_client/dropsonde/error_pb2.py @@ -13,14 +13,14 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0b\x65rror.proto\x12\x06\x65vents\"6\n\x05\x45rror\x12\x0e\n\x06source\x18\x01 \x02(\t\x12\x0c\n\x04\x63ode\x18\x02 \x02(\x05\x12\x0f\n\x07message\x18\x03 \x02(\tB1\n!org.cloudfoundry.dropsonde.eventsB\x0c\x45rrorFactory') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0b\x65rror.proto\x12\x06\x65vents\"6\n\x05\x45rror\x12\x0e\n\x06source\x18\x01 \x02(\t\x12\x0c\n\x04\x63ode\x18\x02 \x02(\x05\x12\x0f\n\x07message\x18\x03 \x02(\tBZ\n!org.cloudfoundry.dropsonde.eventsB\x0c\x45rrorFactoryZ\'github.com/cloudfoundry/sonde-go/events') _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'error_pb2', globals()) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n!org.cloudfoundry.dropsonde.eventsB\014ErrorFactory' + DESCRIPTOR._serialized_options = b'\n!org.cloudfoundry.dropsonde.eventsB\014ErrorFactoryZ\'github.com/cloudfoundry/sonde-go/events' _ERROR._serialized_start=23 _ERROR._serialized_end=77 # @@protoc_insertion_point(module_scope) diff --git a/cloudfoundry_client/dropsonde/http_pb2.py b/cloudfoundry_client/dropsonde/http_pb2.py index 1952842..d6b0eb2 100644 --- a/cloudfoundry_client/dropsonde/http_pb2.py +++ b/cloudfoundry_client/dropsonde/http_pb2.py @@ -11,21 +11,21 @@ _sym_db = _symbol_database.Default() -import uuid_pb2 as uuid__pb2 +import cloudfoundry_client.dropsonde.uuid_pb2 as uuid__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nhttp.proto\x12\x06\x65vents\x1a\nuuid.proto\"\xe8\x02\n\rHttpStartStop\x12\x16\n\x0estartTimestamp\x18\x01 \x02(\x03\x12\x15\n\rstopTimestamp\x18\x02 \x02(\x03\x12\x1f\n\trequestId\x18\x03 \x02(\x0b\x32\x0c.events.UUID\x12\"\n\x08peerType\x18\x04 \x02(\x0e\x32\x10.events.PeerType\x12\x1e\n\x06method\x18\x05 \x02(\x0e\x32\x0e.events.Method\x12\x0b\n\x03uri\x18\x06 \x02(\t\x12\x15\n\rremoteAddress\x18\x07 \x02(\t\x12\x11\n\tuserAgent\x18\x08 \x02(\t\x12\x12\n\nstatusCode\x18\t \x02(\x05\x12\x15\n\rcontentLength\x18\n \x02(\x03\x12#\n\rapplicationId\x18\x0c \x01(\x0b\x32\x0c.events.UUID\x12\x15\n\rinstanceIndex\x18\r \x01(\x05\x12\x12\n\ninstanceId\x18\x0e \x01(\t\x12\x11\n\tforwarded\x18\x0f \x03(\t*\"\n\x08PeerType\x12\n\n\x06\x43lient\x10\x01\x12\n\n\x06Server\x10\x02*\xc6\x04\n\x06Method\x12\x07\n\x03GET\x10\x01\x12\x08\n\x04POST\x10\x02\x12\x07\n\x03PUT\x10\x03\x12\n\n\x06\x44\x45LETE\x10\x04\x12\x08\n\x04HEAD\x10\x05\x12\x07\n\x03\x41\x43L\x10\x06\x12\x14\n\x10\x42\x41SELINE_CONTROL\x10\x07\x12\x08\n\x04\x42IND\x10\x08\x12\x0b\n\x07\x43HECKIN\x10\t\x12\x0c\n\x08\x43HECKOUT\x10\n\x12\x0b\n\x07\x43ONNECT\x10\x0b\x12\x08\n\x04\x43OPY\x10\x0c\x12\t\n\x05\x44\x45\x42UG\x10\r\x12\t\n\x05LABEL\x10\x0e\x12\x08\n\x04LINK\x10\x0f\x12\x08\n\x04LOCK\x10\x10\x12\t\n\x05MERGE\x10\x11\x12\x0e\n\nMKACTIVITY\x10\x12\x12\x0e\n\nMKCALENDAR\x10\x13\x12\t\n\x05MKCOL\x10\x14\x12\x11\n\rMKREDIRECTREF\x10\x15\x12\x0f\n\x0bMKWORKSPACE\x10\x16\x12\x08\n\x04MOVE\x10\x17\x12\x0b\n\x07OPTIONS\x10\x18\x12\x0e\n\nORDERPATCH\x10\x19\x12\t\n\x05PATCH\x10\x1a\x12\x07\n\x03PRI\x10\x1b\x12\x0c\n\x08PROPFIND\x10\x1c\x12\r\n\tPROPPATCH\x10\x1d\x12\n\n\x06REBIND\x10\x1e\x12\n\n\x06REPORT\x10\x1f\x12\n\n\x06SEARCH\x10 \x12\x0e\n\nSHOWMETHOD\x10!\x12\r\n\tSPACEJUMP\x10\"\x12\x0e\n\nTEXTSEARCH\x10#\x12\t\n\x05TRACE\x10$\x12\t\n\x05TRACK\x10%\x12\n\n\x06UNBIND\x10&\x12\x0e\n\nUNCHECKOUT\x10\'\x12\n\n\x06UNLINK\x10(\x12\n\n\x06UNLOCK\x10)\x12\n\n\x06UPDATE\x10*\x12\x15\n\x11UPDATEREDIRECTREF\x10+\x12\x13\n\x0fVERSION_CONTROL\x10,B0\n!org.cloudfoundry.dropsonde.eventsB\x0bHttpFactory') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nhttp.proto\x12\x06\x65vents\x1a\nuuid.proto\"\xff\x02\n\rHttpStartStop\x12\x16\n\x0estartTimestamp\x18\x01 \x02(\x03\x12\x15\n\rstopTimestamp\x18\x02 \x02(\x03\x12\x1f\n\trequestId\x18\x03 \x02(\x0b\x32\x0c.events.UUID\x12\"\n\x08peerType\x18\x04 \x02(\x0e\x32\x10.events.PeerType\x12\x1e\n\x06method\x18\x05 \x02(\x0e\x32\x0e.events.Method\x12\x0b\n\x03uri\x18\x06 \x02(\t\x12\x15\n\rremoteAddress\x18\x07 \x02(\t\x12\x11\n\tuserAgent\x18\x08 \x02(\t\x12\x12\n\nstatusCode\x18\t \x02(\x05\x12\x15\n\rcontentLength\x18\n \x02(\x03\x12#\n\rapplicationId\x18\x0c \x01(\x0b\x32\x0c.events.UUID\x12\x15\n\rinstanceIndex\x18\r \x01(\x05\x12\x12\n\ninstanceId\x18\x0e \x01(\t\x12\x11\n\tforwarded\x18\x0f \x03(\tJ\x04\x08\x0b\x10\x0cR\x0fparentRequestId*\"\n\x08PeerType\x12\n\n\x06\x43lient\x10\x01\x12\n\n\x06Server\x10\x02*\xc6\x04\n\x06Method\x12\x07\n\x03GET\x10\x01\x12\x08\n\x04POST\x10\x02\x12\x07\n\x03PUT\x10\x03\x12\n\n\x06\x44\x45LETE\x10\x04\x12\x08\n\x04HEAD\x10\x05\x12\x07\n\x03\x41\x43L\x10\x06\x12\x14\n\x10\x42\x41SELINE_CONTROL\x10\x07\x12\x08\n\x04\x42IND\x10\x08\x12\x0b\n\x07\x43HECKIN\x10\t\x12\x0c\n\x08\x43HECKOUT\x10\n\x12\x0b\n\x07\x43ONNECT\x10\x0b\x12\x08\n\x04\x43OPY\x10\x0c\x12\t\n\x05\x44\x45\x42UG\x10\r\x12\t\n\x05LABEL\x10\x0e\x12\x08\n\x04LINK\x10\x0f\x12\x08\n\x04LOCK\x10\x10\x12\t\n\x05MERGE\x10\x11\x12\x0e\n\nMKACTIVITY\x10\x12\x12\x0e\n\nMKCALENDAR\x10\x13\x12\t\n\x05MKCOL\x10\x14\x12\x11\n\rMKREDIRECTREF\x10\x15\x12\x0f\n\x0bMKWORKSPACE\x10\x16\x12\x08\n\x04MOVE\x10\x17\x12\x0b\n\x07OPTIONS\x10\x18\x12\x0e\n\nORDERPATCH\x10\x19\x12\t\n\x05PATCH\x10\x1a\x12\x07\n\x03PRI\x10\x1b\x12\x0c\n\x08PROPFIND\x10\x1c\x12\r\n\tPROPPATCH\x10\x1d\x12\n\n\x06REBIND\x10\x1e\x12\n\n\x06REPORT\x10\x1f\x12\n\n\x06SEARCH\x10 \x12\x0e\n\nSHOWMETHOD\x10!\x12\r\n\tSPACEJUMP\x10\"\x12\x0e\n\nTEXTSEARCH\x10#\x12\t\n\x05TRACE\x10$\x12\t\n\x05TRACK\x10%\x12\n\n\x06UNBIND\x10&\x12\x0e\n\nUNCHECKOUT\x10\'\x12\n\n\x06UNLINK\x10(\x12\n\n\x06UNLOCK\x10)\x12\n\n\x06UPDATE\x10*\x12\x15\n\x11UPDATEREDIRECTREF\x10+\x12\x13\n\x0fVERSION_CONTROL\x10,BY\n!org.cloudfoundry.dropsonde.eventsB\x0bHttpFactoryZ\'github.com/cloudfoundry/sonde-go/events') _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'http_pb2', globals()) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n!org.cloudfoundry.dropsonde.eventsB\013HttpFactory' - _PEERTYPE._serialized_start=397 - _PEERTYPE._serialized_end=431 - _METHOD._serialized_start=434 - _METHOD._serialized_end=1016 + DESCRIPTOR._serialized_options = b'\n!org.cloudfoundry.dropsonde.eventsB\013HttpFactoryZ\'github.com/cloudfoundry/sonde-go/events' + _PEERTYPE._serialized_start=420 + _PEERTYPE._serialized_end=454 + _METHOD._serialized_start=457 + _METHOD._serialized_end=1039 _HTTPSTARTSTOP._serialized_start=35 - _HTTPSTARTSTOP._serialized_end=395 + _HTTPSTARTSTOP._serialized_end=418 # @@protoc_insertion_point(module_scope) diff --git a/cloudfoundry_client/dropsonde/log_pb2.py b/cloudfoundry_client/dropsonde/log_pb2.py index da7f46d..3c76fbd 100644 --- a/cloudfoundry_client/dropsonde/log_pb2.py +++ b/cloudfoundry_client/dropsonde/log_pb2.py @@ -13,14 +13,14 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\tlog.proto\x12\x06\x65vents\"\xc5\x01\n\nLogMessage\x12\x0f\n\x07message\x18\x01 \x02(\x0c\x12\x34\n\x0cmessage_type\x18\x02 \x02(\x0e\x32\x1e.events.LogMessage.MessageType\x12\x11\n\ttimestamp\x18\x03 \x02(\x03\x12\x0e\n\x06\x61pp_id\x18\x04 \x01(\t\x12\x13\n\x0bsource_type\x18\x05 \x01(\t\x12\x17\n\x0fsource_instance\x18\x06 \x01(\t\"\x1f\n\x0bMessageType\x12\x07\n\x03OUT\x10\x01\x12\x07\n\x03\x45RR\x10\x02\x42/\n!org.cloudfoundry.dropsonde.eventsB\nLogFactory') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\tlog.proto\x12\x06\x65vents\"\xc5\x01\n\nLogMessage\x12\x0f\n\x07message\x18\x01 \x02(\x0c\x12\x34\n\x0cmessage_type\x18\x02 \x02(\x0e\x32\x1e.events.LogMessage.MessageType\x12\x11\n\ttimestamp\x18\x03 \x02(\x03\x12\x0e\n\x06\x61pp_id\x18\x04 \x01(\t\x12\x13\n\x0bsource_type\x18\x05 \x01(\t\x12\x17\n\x0fsource_instance\x18\x06 \x01(\t\"\x1f\n\x0bMessageType\x12\x07\n\x03OUT\x10\x01\x12\x07\n\x03\x45RR\x10\x02\x42X\n!org.cloudfoundry.dropsonde.eventsB\nLogFactoryZ\'github.com/cloudfoundry/sonde-go/events') _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'log_pb2', globals()) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n!org.cloudfoundry.dropsonde.eventsB\nLogFactory' + DESCRIPTOR._serialized_options = b'\n!org.cloudfoundry.dropsonde.eventsB\nLogFactoryZ\'github.com/cloudfoundry/sonde-go/events' _LOGMESSAGE._serialized_start=22 _LOGMESSAGE._serialized_end=219 _LOGMESSAGE_MESSAGETYPE._serialized_start=188 diff --git a/cloudfoundry_client/dropsonde/metric_pb2.py b/cloudfoundry_client/dropsonde/metric_pb2.py index 3db344a..49b8d4f 100644 --- a/cloudfoundry_client/dropsonde/metric_pb2.py +++ b/cloudfoundry_client/dropsonde/metric_pb2.py @@ -13,14 +13,14 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cmetric.proto\x12\x06\x65vents\"8\n\x0bValueMetric\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\r\n\x05value\x18\x02 \x02(\x01\x12\x0c\n\x04unit\x18\x03 \x02(\t\":\n\x0c\x43ounterEvent\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\r\n\x05\x64\x65lta\x18\x02 \x02(\x04\x12\r\n\x05total\x18\x03 \x01(\x04\"\xb0\x01\n\x0f\x43ontainerMetric\x12\x15\n\rapplicationId\x18\x01 \x02(\t\x12\x15\n\rinstanceIndex\x18\x02 \x02(\x05\x12\x15\n\rcpuPercentage\x18\x03 \x02(\x01\x12\x13\n\x0bmemoryBytes\x18\x04 \x02(\x04\x12\x11\n\tdiskBytes\x18\x05 \x02(\x04\x12\x18\n\x10memoryBytesQuota\x18\x06 \x01(\x04\x12\x16\n\x0e\x64iskBytesQuota\x18\x07 \x01(\x04\x42\x32\n!org.cloudfoundry.dropsonde.eventsB\rMetricFactory') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cmetric.proto\x12\x06\x65vents\"8\n\x0bValueMetric\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\r\n\x05value\x18\x02 \x02(\x01\x12\x0c\n\x04unit\x18\x03 \x02(\t\":\n\x0c\x43ounterEvent\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\r\n\x05\x64\x65lta\x18\x02 \x02(\x04\x12\r\n\x05total\x18\x03 \x01(\x04\"\xb0\x01\n\x0f\x43ontainerMetric\x12\x15\n\rapplicationId\x18\x01 \x02(\t\x12\x15\n\rinstanceIndex\x18\x02 \x02(\x05\x12\x15\n\rcpuPercentage\x18\x03 \x02(\x01\x12\x13\n\x0bmemoryBytes\x18\x04 \x02(\x04\x12\x11\n\tdiskBytes\x18\x05 \x02(\x04\x12\x18\n\x10memoryBytesQuota\x18\x06 \x01(\x04\x12\x16\n\x0e\x64iskBytesQuota\x18\x07 \x01(\x04\x42[\n!org.cloudfoundry.dropsonde.eventsB\rMetricFactoryZ\'github.com/cloudfoundry/sonde-go/events') _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'metric_pb2', globals()) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n!org.cloudfoundry.dropsonde.eventsB\rMetricFactory' + DESCRIPTOR._serialized_options = b'\n!org.cloudfoundry.dropsonde.eventsB\rMetricFactoryZ\'github.com/cloudfoundry/sonde-go/events' _VALUEMETRIC._serialized_start=24 _VALUEMETRIC._serialized_end=80 _COUNTEREVENT._serialized_start=82 diff --git a/cloudfoundry_client/dropsonde/uuid_pb2.py b/cloudfoundry_client/dropsonde/uuid_pb2.py index f4d5d4a..fc002ea 100644 --- a/cloudfoundry_client/dropsonde/uuid_pb2.py +++ b/cloudfoundry_client/dropsonde/uuid_pb2.py @@ -13,14 +13,14 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nuuid.proto\x12\x06\x65vents\"!\n\x04UUID\x12\x0b\n\x03low\x18\x01 \x02(\x04\x12\x0c\n\x04high\x18\x02 \x02(\x04\x42\x30\n!org.cloudfoundry.dropsonde.eventsB\x0bUuidFactory') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nuuid.proto\x12\x06\x65vents\"!\n\x04UUID\x12\x0b\n\x03low\x18\x01 \x02(\x04\x12\x0c\n\x04high\x18\x02 \x02(\x04\x42Y\n!org.cloudfoundry.dropsonde.eventsB\x0bUuidFactoryZ\'github.com/cloudfoundry/sonde-go/events') _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'uuid_pb2', globals()) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n!org.cloudfoundry.dropsonde.eventsB\013UuidFactory' + DESCRIPTOR._serialized_options = b'\n!org.cloudfoundry.dropsonde.eventsB\013UuidFactoryZ\'github.com/cloudfoundry/sonde-go/events' _UUID._serialized_start=22 _UUID._serialized_end=55 # @@protoc_insertion_point(module_scope) diff --git a/poetry.lock b/poetry.lock index d894530..a264769 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,106 +1,104 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" -version = "2.4.4" +version = "2.6.1" description = "Happy Eyeballs for asyncio" optional = false -python-versions = ">=3.8" -groups = ["main"] +python-versions = ">=3.9" files = [ - {file = "aiohappyeyeballs-2.4.4-py3-none-any.whl", hash = "sha256:a980909d50efcd44795c4afeca523296716d50cd756ddca6af8c65b996e27de8"}, - {file = "aiohappyeyeballs-2.4.4.tar.gz", hash = "sha256:5fdd7d87889c63183afc18ce9271f9b0a7d32c2303e394468dd45d514a757745"}, + {file = "aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8"}, + {file = "aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558"}, ] [[package]] name = "aiohttp" -version = "3.11.16" +version = "3.11.18" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ - {file = "aiohttp-3.11.16-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb46bb0f24813e6cede6cc07b1961d4b04f331f7112a23b5e21f567da4ee50aa"}, - {file = "aiohttp-3.11.16-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:54eb3aead72a5c19fad07219acd882c1643a1027fbcdefac9b502c267242f955"}, - {file = "aiohttp-3.11.16-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:38bea84ee4fe24ebcc8edeb7b54bf20f06fd53ce4d2cc8b74344c5b9620597fd"}, - {file = "aiohttp-3.11.16-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0666afbe984f6933fe72cd1f1c3560d8c55880a0bdd728ad774006eb4241ecd"}, - {file = "aiohttp-3.11.16-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ba92a2d9ace559a0a14b03d87f47e021e4fa7681dc6970ebbc7b447c7d4b7cd"}, - {file = "aiohttp-3.11.16-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ad1d59fd7114e6a08c4814983bb498f391c699f3c78712770077518cae63ff7"}, - {file = "aiohttp-3.11.16-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b88a2bf26965f2015a771381624dd4b0839034b70d406dc74fd8be4cc053e3"}, - {file = "aiohttp-3.11.16-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:576f5ca28d1b3276026f7df3ec841ae460e0fc3aac2a47cbf72eabcfc0f102e1"}, - {file = "aiohttp-3.11.16-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a2a450bcce4931b295fc0848f384834c3f9b00edfc2150baafb4488c27953de6"}, - {file = "aiohttp-3.11.16-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:37dcee4906454ae377be5937ab2a66a9a88377b11dd7c072df7a7c142b63c37c"}, - {file = "aiohttp-3.11.16-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4d0c970c0d602b1017e2067ff3b7dac41c98fef4f7472ec2ea26fd8a4e8c2149"}, - {file = "aiohttp-3.11.16-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:004511d3413737700835e949433536a2fe95a7d0297edd911a1e9705c5b5ea43"}, - {file = "aiohttp-3.11.16-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:c15b2271c44da77ee9d822552201180779e5e942f3a71fb74e026bf6172ff287"}, - {file = "aiohttp-3.11.16-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ad9509ffb2396483ceacb1eee9134724443ee45b92141105a4645857244aecc8"}, - {file = "aiohttp-3.11.16-cp310-cp310-win32.whl", hash = "sha256:634d96869be6c4dc232fc503e03e40c42d32cfaa51712aee181e922e61d74814"}, - {file = "aiohttp-3.11.16-cp310-cp310-win_amd64.whl", hash = "sha256:938f756c2b9374bbcc262a37eea521d8a0e6458162f2a9c26329cc87fdf06534"}, - {file = "aiohttp-3.11.16-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8cb0688a8d81c63d716e867d59a9ccc389e97ac7037ebef904c2b89334407180"}, - {file = "aiohttp-3.11.16-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ad1fb47da60ae1ddfb316f0ff16d1f3b8e844d1a1e154641928ea0583d486ed"}, - {file = "aiohttp-3.11.16-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:df7db76400bf46ec6a0a73192b14c8295bdb9812053f4fe53f4e789f3ea66bbb"}, - {file = "aiohttp-3.11.16-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc3a145479a76ad0ed646434d09216d33d08eef0d8c9a11f5ae5cdc37caa3540"}, - {file = "aiohttp-3.11.16-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d007aa39a52d62373bd23428ba4a2546eed0e7643d7bf2e41ddcefd54519842c"}, - {file = "aiohttp-3.11.16-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6ddd90d9fb4b501c97a4458f1c1720e42432c26cb76d28177c5b5ad4e332601"}, - {file = "aiohttp-3.11.16-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a2f451849e6b39e5c226803dcacfa9c7133e9825dcefd2f4e837a2ec5a3bb98"}, - {file = "aiohttp-3.11.16-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8df6612df74409080575dca38a5237282865408016e65636a76a2eb9348c2567"}, - {file = "aiohttp-3.11.16-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:78e6e23b954644737e385befa0deb20233e2dfddf95dd11e9db752bdd2a294d3"}, - {file = "aiohttp-3.11.16-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:696ef00e8a1f0cec5e30640e64eca75d8e777933d1438f4facc9c0cdf288a810"}, - {file = "aiohttp-3.11.16-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e3538bc9fe1b902bef51372462e3d7c96fce2b566642512138a480b7adc9d508"}, - {file = "aiohttp-3.11.16-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3ab3367bb7f61ad18793fea2ef71f2d181c528c87948638366bf1de26e239183"}, - {file = "aiohttp-3.11.16-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:56a3443aca82abda0e07be2e1ecb76a050714faf2be84256dae291182ba59049"}, - {file = "aiohttp-3.11.16-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:61c721764e41af907c9d16b6daa05a458f066015abd35923051be8705108ed17"}, - {file = "aiohttp-3.11.16-cp311-cp311-win32.whl", hash = "sha256:3e061b09f6fa42997cf627307f220315e313ece74907d35776ec4373ed718b86"}, - {file = "aiohttp-3.11.16-cp311-cp311-win_amd64.whl", hash = "sha256:745f1ed5e2c687baefc3c5e7b4304e91bf3e2f32834d07baaee243e349624b24"}, - {file = "aiohttp-3.11.16-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:911a6e91d08bb2c72938bc17f0a2d97864c531536b7832abee6429d5296e5b27"}, - {file = "aiohttp-3.11.16-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac13b71761e49d5f9e4d05d33683bbafef753e876e8e5a7ef26e937dd766713"}, - {file = "aiohttp-3.11.16-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fd36c119c5d6551bce374fcb5c19269638f8d09862445f85a5a48596fd59f4bb"}, - {file = "aiohttp-3.11.16-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d489d9778522fbd0f8d6a5c6e48e3514f11be81cb0a5954bdda06f7e1594b321"}, - {file = "aiohttp-3.11.16-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69a2cbd61788d26f8f1e626e188044834f37f6ae3f937bd9f08b65fc9d7e514e"}, - {file = "aiohttp-3.11.16-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd464ba806e27ee24a91362ba3621bfc39dbbb8b79f2e1340201615197370f7c"}, - {file = "aiohttp-3.11.16-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce63ae04719513dd2651202352a2beb9f67f55cb8490c40f056cea3c5c355ce"}, - {file = "aiohttp-3.11.16-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09b00dd520d88eac9d1768439a59ab3d145065c91a8fab97f900d1b5f802895e"}, - {file = "aiohttp-3.11.16-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7f6428fee52d2bcf96a8aa7b62095b190ee341ab0e6b1bcf50c615d7966fd45b"}, - {file = "aiohttp-3.11.16-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:13ceac2c5cdcc3f64b9015710221ddf81c900c5febc505dbd8f810e770011540"}, - {file = "aiohttp-3.11.16-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fadbb8f1d4140825069db3fedbbb843290fd5f5bc0a5dbd7eaf81d91bf1b003b"}, - {file = "aiohttp-3.11.16-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6a792ce34b999fbe04a7a71a90c74f10c57ae4c51f65461a411faa70e154154e"}, - {file = "aiohttp-3.11.16-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f4065145bf69de124accdd17ea5f4dc770da0a6a6e440c53f6e0a8c27b3e635c"}, - {file = "aiohttp-3.11.16-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa73e8c2656a3653ae6c307b3f4e878a21f87859a9afab228280ddccd7369d71"}, - {file = "aiohttp-3.11.16-cp312-cp312-win32.whl", hash = "sha256:f244b8e541f414664889e2c87cac11a07b918cb4b540c36f7ada7bfa76571ea2"}, - {file = "aiohttp-3.11.16-cp312-cp312-win_amd64.whl", hash = "sha256:23a15727fbfccab973343b6d1b7181bfb0b4aa7ae280f36fd2f90f5476805682"}, - {file = "aiohttp-3.11.16-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a3814760a1a700f3cfd2f977249f1032301d0a12c92aba74605cfa6ce9f78489"}, - {file = "aiohttp-3.11.16-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9b751a6306f330801665ae69270a8a3993654a85569b3469662efaad6cf5cc50"}, - {file = "aiohttp-3.11.16-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ad497f38a0d6c329cb621774788583ee12321863cd4bd9feee1effd60f2ad133"}, - {file = "aiohttp-3.11.16-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca37057625693d097543bd88076ceebeb248291df9d6ca8481349efc0b05dcd0"}, - {file = "aiohttp-3.11.16-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5abcbba9f4b463a45c8ca8b7720891200658f6f46894f79517e6cd11f3405ca"}, - {file = "aiohttp-3.11.16-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f420bfe862fb357a6d76f2065447ef6f484bc489292ac91e29bc65d2d7a2c84d"}, - {file = "aiohttp-3.11.16-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58ede86453a6cf2d6ce40ef0ca15481677a66950e73b0a788917916f7e35a0bb"}, - {file = "aiohttp-3.11.16-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fdec0213244c39973674ca2a7f5435bf74369e7d4e104d6c7473c81c9bcc8c4"}, - {file = "aiohttp-3.11.16-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:72b1b03fb4655c1960403c131740755ec19c5898c82abd3961c364c2afd59fe7"}, - {file = "aiohttp-3.11.16-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:780df0d837276276226a1ff803f8d0fa5f8996c479aeef52eb040179f3156cbd"}, - {file = "aiohttp-3.11.16-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ecdb8173e6c7aa09eee342ac62e193e6904923bd232e76b4157ac0bfa670609f"}, - {file = "aiohttp-3.11.16-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a6db7458ab89c7d80bc1f4e930cc9df6edee2200127cfa6f6e080cf619eddfbd"}, - {file = "aiohttp-3.11.16-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2540ddc83cc724b13d1838026f6a5ad178510953302a49e6d647f6e1de82bc34"}, - {file = "aiohttp-3.11.16-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3b4e6db8dc4879015b9955778cfb9881897339c8fab7b3676f8433f849425913"}, - {file = "aiohttp-3.11.16-cp313-cp313-win32.whl", hash = "sha256:493910ceb2764f792db4dc6e8e4b375dae1b08f72e18e8f10f18b34ca17d0979"}, - {file = "aiohttp-3.11.16-cp313-cp313-win_amd64.whl", hash = "sha256:42864e70a248f5f6a49fdaf417d9bc62d6e4d8ee9695b24c5916cb4bb666c802"}, - {file = "aiohttp-3.11.16-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bbcba75fe879ad6fd2e0d6a8d937f34a571f116a0e4db37df8079e738ea95c71"}, - {file = "aiohttp-3.11.16-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:87a6e922b2b2401e0b0cf6b976b97f11ec7f136bfed445e16384fbf6fd5e8602"}, - {file = "aiohttp-3.11.16-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccf10f16ab498d20e28bc2b5c1306e9c1512f2840f7b6a67000a517a4b37d5ee"}, - {file = "aiohttp-3.11.16-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb3d0cc5cdb926090748ea60172fa8a213cec728bd6c54eae18b96040fcd6227"}, - {file = "aiohttp-3.11.16-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d07502cc14ecd64f52b2a74ebbc106893d9a9717120057ea9ea1fd6568a747e7"}, - {file = "aiohttp-3.11.16-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:776c8e959a01e5e8321f1dec77964cb6101020a69d5a94cd3d34db6d555e01f7"}, - {file = "aiohttp-3.11.16-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0902e887b0e1d50424112f200eb9ae3dfed6c0d0a19fc60f633ae5a57c809656"}, - {file = "aiohttp-3.11.16-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e87fd812899aa78252866ae03a048e77bd11b80fb4878ce27c23cade239b42b2"}, - {file = "aiohttp-3.11.16-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0a950c2eb8ff17361abd8c85987fd6076d9f47d040ebffce67dce4993285e973"}, - {file = "aiohttp-3.11.16-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:c10d85e81d0b9ef87970ecbdbfaeec14a361a7fa947118817fcea8e45335fa46"}, - {file = "aiohttp-3.11.16-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:7951decace76a9271a1ef181b04aa77d3cc309a02a51d73826039003210bdc86"}, - {file = "aiohttp-3.11.16-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14461157d8426bcb40bd94deb0450a6fa16f05129f7da546090cebf8f3123b0f"}, - {file = "aiohttp-3.11.16-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9756d9b9d4547e091f99d554fbba0d2a920aab98caa82a8fb3d3d9bee3c9ae85"}, - {file = "aiohttp-3.11.16-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:87944bd16b7fe6160607f6a17808abd25f17f61ae1e26c47a491b970fb66d8cb"}, - {file = "aiohttp-3.11.16-cp39-cp39-win32.whl", hash = "sha256:92b7ee222e2b903e0a4b329a9943d432b3767f2d5029dbe4ca59fb75223bbe2e"}, - {file = "aiohttp-3.11.16-cp39-cp39-win_amd64.whl", hash = "sha256:17ae4664031aadfbcb34fd40ffd90976671fa0c0286e6c4113989f78bebab37a"}, - {file = "aiohttp-3.11.16.tar.gz", hash = "sha256:16f8a2c9538c14a557b4d309ed4d0a7c60f0253e8ed7b6c9a2859a7582f8b1b8"}, + {file = "aiohttp-3.11.18-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:96264854fedbea933a9ca4b7e0c745728f01380691687b7365d18d9e977179c4"}, + {file = "aiohttp-3.11.18-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9602044ff047043430452bc3a2089743fa85da829e6fc9ee0025351d66c332b6"}, + {file = "aiohttp-3.11.18-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5691dc38750fcb96a33ceef89642f139aa315c8a193bbd42a0c33476fd4a1609"}, + {file = "aiohttp-3.11.18-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:554c918ec43f8480b47a5ca758e10e793bd7410b83701676a4782672d670da55"}, + {file = "aiohttp-3.11.18-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a4076a2b3ba5b004b8cffca6afe18a3b2c5c9ef679b4d1e9859cf76295f8d4f"}, + {file = "aiohttp-3.11.18-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:767a97e6900edd11c762be96d82d13a1d7c4fc4b329f054e88b57cdc21fded94"}, + {file = "aiohttp-3.11.18-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0ddc9337a0fb0e727785ad4f41163cc314376e82b31846d3835673786420ef1"}, + {file = "aiohttp-3.11.18-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f414f37b244f2a97e79b98d48c5ff0789a0b4b4609b17d64fa81771ad780e415"}, + {file = "aiohttp-3.11.18-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fdb239f47328581e2ec7744ab5911f97afb10752332a6dd3d98e14e429e1a9e7"}, + {file = "aiohttp-3.11.18-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f2c50bad73ed629cc326cc0f75aed8ecfb013f88c5af116f33df556ed47143eb"}, + {file = "aiohttp-3.11.18-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a8d8f20c39d3fa84d1c28cdb97f3111387e48209e224408e75f29c6f8e0861d"}, + {file = "aiohttp-3.11.18-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:106032eaf9e62fd6bc6578c8b9e6dc4f5ed9a5c1c7fb2231010a1b4304393421"}, + {file = "aiohttp-3.11.18-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:b491e42183e8fcc9901d8dcd8ae644ff785590f1727f76ca86e731c61bfe6643"}, + {file = "aiohttp-3.11.18-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ad8c745ff9460a16b710e58e06a9dec11ebc0d8f4dd82091cefb579844d69868"}, + {file = "aiohttp-3.11.18-cp310-cp310-win32.whl", hash = "sha256:8e57da93e24303a883146510a434f0faf2f1e7e659f3041abc4e3fb3f6702a9f"}, + {file = "aiohttp-3.11.18-cp310-cp310-win_amd64.whl", hash = "sha256:cc93a4121d87d9f12739fc8fab0a95f78444e571ed63e40bfc78cd5abe700ac9"}, + {file = "aiohttp-3.11.18-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:427fdc56ccb6901ff8088544bde47084845ea81591deb16f957897f0f0ba1be9"}, + {file = "aiohttp-3.11.18-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c828b6d23b984255b85b9b04a5b963a74278b7356a7de84fda5e3b76866597b"}, + {file = "aiohttp-3.11.18-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5c2eaa145bb36b33af1ff2860820ba0589e165be4ab63a49aebfd0981c173b66"}, + {file = "aiohttp-3.11.18-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d518ce32179f7e2096bf4e3e8438cf445f05fedd597f252de9f54c728574756"}, + {file = "aiohttp-3.11.18-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0700055a6e05c2f4711011a44364020d7a10fbbcd02fbf3e30e8f7e7fddc8717"}, + {file = "aiohttp-3.11.18-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8bd1cde83e4684324e6ee19adfc25fd649d04078179890be7b29f76b501de8e4"}, + {file = "aiohttp-3.11.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73b8870fe1c9a201b8c0d12c94fe781b918664766728783241a79e0468427e4f"}, + {file = "aiohttp-3.11.18-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25557982dd36b9e32c0a3357f30804e80790ec2c4d20ac6bcc598533e04c6361"}, + {file = "aiohttp-3.11.18-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7e889c9df381a2433802991288a61e5a19ceb4f61bd14f5c9fa165655dcb1fd1"}, + {file = "aiohttp-3.11.18-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9ea345fda05bae217b6cce2acf3682ce3b13d0d16dd47d0de7080e5e21362421"}, + {file = "aiohttp-3.11.18-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9f26545b9940c4b46f0a9388fd04ee3ad7064c4017b5a334dd450f616396590e"}, + {file = "aiohttp-3.11.18-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3a621d85e85dccabd700294494d7179ed1590b6d07a35709bb9bd608c7f5dd1d"}, + {file = "aiohttp-3.11.18-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9c23fd8d08eb9c2af3faeedc8c56e134acdaf36e2117ee059d7defa655130e5f"}, + {file = "aiohttp-3.11.18-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9e6b0e519067caa4fd7fb72e3e8002d16a68e84e62e7291092a5433763dc0dd"}, + {file = "aiohttp-3.11.18-cp311-cp311-win32.whl", hash = "sha256:122f3e739f6607e5e4c6a2f8562a6f476192a682a52bda8b4c6d4254e1138f4d"}, + {file = "aiohttp-3.11.18-cp311-cp311-win_amd64.whl", hash = "sha256:e6f3c0a3a1e73e88af384b2e8a0b9f4fb73245afd47589df2afcab6b638fa0e6"}, + {file = "aiohttp-3.11.18-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:63d71eceb9cad35d47d71f78edac41fcd01ff10cacaa64e473d1aec13fa02df2"}, + {file = "aiohttp-3.11.18-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d1929da615840969929e8878d7951b31afe0bac883d84418f92e5755d7b49508"}, + {file = "aiohttp-3.11.18-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d0aebeb2392f19b184e3fdd9e651b0e39cd0f195cdb93328bd124a1d455cd0e"}, + {file = "aiohttp-3.11.18-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3849ead845e8444f7331c284132ab314b4dac43bfae1e3cf350906d4fff4620f"}, + {file = "aiohttp-3.11.18-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e8452ad6b2863709f8b3d615955aa0807bc093c34b8e25b3b52097fe421cb7f"}, + {file = "aiohttp-3.11.18-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b8d2b42073611c860a37f718b3d61ae8b4c2b124b2e776e2c10619d920350ec"}, + {file = "aiohttp-3.11.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40fbf91f6a0ac317c0a07eb328a1384941872f6761f2e6f7208b63c4cc0a7ff6"}, + {file = "aiohttp-3.11.18-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ff5625413fec55216da5eaa011cf6b0a2ed67a565914a212a51aa3755b0009"}, + {file = "aiohttp-3.11.18-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7f33a92a2fde08e8c6b0c61815521324fc1612f397abf96eed86b8e31618fdb4"}, + {file = "aiohttp-3.11.18-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:11d5391946605f445ddafda5eab11caf310f90cdda1fd99865564e3164f5cff9"}, + {file = "aiohttp-3.11.18-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3cc314245deb311364884e44242e00c18b5896e4fe6d5f942e7ad7e4cb640adb"}, + {file = "aiohttp-3.11.18-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0f421843b0f70740772228b9e8093289924359d306530bcd3926f39acbe1adda"}, + {file = "aiohttp-3.11.18-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e220e7562467dc8d589e31c1acd13438d82c03d7f385c9cd41a3f6d1d15807c1"}, + {file = "aiohttp-3.11.18-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ab2ef72f8605046115bc9aa8e9d14fd49086d405855f40b79ed9e5c1f9f4faea"}, + {file = "aiohttp-3.11.18-cp312-cp312-win32.whl", hash = "sha256:12a62691eb5aac58d65200c7ae94d73e8a65c331c3a86a2e9670927e94339ee8"}, + {file = "aiohttp-3.11.18-cp312-cp312-win_amd64.whl", hash = "sha256:364329f319c499128fd5cd2d1c31c44f234c58f9b96cc57f743d16ec4f3238c8"}, + {file = "aiohttp-3.11.18-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:474215ec618974054cf5dc465497ae9708543cbfc312c65212325d4212525811"}, + {file = "aiohttp-3.11.18-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6ced70adf03920d4e67c373fd692123e34d3ac81dfa1c27e45904a628567d804"}, + {file = "aiohttp-3.11.18-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2d9f6c0152f8d71361905aaf9ed979259537981f47ad099c8b3d81e0319814bd"}, + {file = "aiohttp-3.11.18-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a35197013ed929c0aed5c9096de1fc5a9d336914d73ab3f9df14741668c0616c"}, + {file = "aiohttp-3.11.18-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:540b8a1f3a424f1af63e0af2d2853a759242a1769f9f1ab053996a392bd70118"}, + {file = "aiohttp-3.11.18-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9e6710ebebfce2ba21cee6d91e7452d1125100f41b906fb5af3da8c78b764c1"}, + {file = "aiohttp-3.11.18-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8af2ef3b4b652ff109f98087242e2ab974b2b2b496304063585e3d78de0b000"}, + {file = "aiohttp-3.11.18-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:28c3f975e5ae3dbcbe95b7e3dcd30e51da561a0a0f2cfbcdea30fc1308d72137"}, + {file = "aiohttp-3.11.18-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c28875e316c7b4c3e745172d882d8a5c835b11018e33432d281211af35794a93"}, + {file = "aiohttp-3.11.18-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:13cd38515568ae230e1ef6919e2e33da5d0f46862943fcda74e7e915096815f3"}, + {file = "aiohttp-3.11.18-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0e2a92101efb9f4c2942252c69c63ddb26d20f46f540c239ccfa5af865197bb8"}, + {file = "aiohttp-3.11.18-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e6d3e32b8753c8d45ac550b11a1090dd66d110d4ef805ffe60fa61495360b3b2"}, + {file = "aiohttp-3.11.18-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ea4cf2488156e0f281f93cc2fd365025efcba3e2d217cbe3df2840f8c73db261"}, + {file = "aiohttp-3.11.18-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d4df95ad522c53f2b9ebc07f12ccd2cb15550941e11a5bbc5ddca2ca56316d7"}, + {file = "aiohttp-3.11.18-cp313-cp313-win32.whl", hash = "sha256:cdd1bbaf1e61f0d94aced116d6e95fe25942f7a5f42382195fd9501089db5d78"}, + {file = "aiohttp-3.11.18-cp313-cp313-win_amd64.whl", hash = "sha256:bdd619c27e44382cf642223f11cfd4d795161362a5a1fc1fa3940397bc89db01"}, + {file = "aiohttp-3.11.18-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:469ac32375d9a716da49817cd26f1916ec787fc82b151c1c832f58420e6d3533"}, + {file = "aiohttp-3.11.18-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3cec21dd68924179258ae14af9f5418c1ebdbba60b98c667815891293902e5e0"}, + {file = "aiohttp-3.11.18-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b426495fb9140e75719b3ae70a5e8dd3a79def0ae3c6c27e012fc59f16544a4a"}, + {file = "aiohttp-3.11.18-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad2f41203e2808616292db5d7170cccf0c9f9c982d02544443c7eb0296e8b0c7"}, + {file = "aiohttp-3.11.18-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5bc0ae0a5e9939e423e065a3e5b00b24b8379f1db46046d7ab71753dfc7dd0e1"}, + {file = "aiohttp-3.11.18-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe7cdd3f7d1df43200e1c80f1aed86bb36033bf65e3c7cf46a2b97a253ef8798"}, + {file = "aiohttp-3.11.18-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5199be2a2f01ffdfa8c3a6f5981205242986b9e63eb8ae03fd18f736e4840721"}, + {file = "aiohttp-3.11.18-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ccec9e72660b10f8e283e91aa0295975c7bd85c204011d9f5eb69310555cf30"}, + {file = "aiohttp-3.11.18-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1596ebf17e42e293cbacc7a24c3e0dc0f8f755b40aff0402cb74c1ff6baec1d3"}, + {file = "aiohttp-3.11.18-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:eab7b040a8a873020113ba814b7db7fa935235e4cbaf8f3da17671baa1024863"}, + {file = "aiohttp-3.11.18-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:5d61df4a05476ff891cff0030329fee4088d40e4dc9b013fac01bc3c745542c2"}, + {file = "aiohttp-3.11.18-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:46533e6792e1410f9801d09fd40cbbff3f3518d1b501d6c3c5b218f427f6ff08"}, + {file = "aiohttp-3.11.18-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c1b90407ced992331dd6d4f1355819ea1c274cc1ee4d5b7046c6761f9ec11829"}, + {file = "aiohttp-3.11.18-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a2fd04ae4971b914e54fe459dd7edbbd3f2ba875d69e057d5e3c8e8cac094935"}, + {file = "aiohttp-3.11.18-cp39-cp39-win32.whl", hash = "sha256:b2f317d1678002eee6fe85670039fb34a757972284614638f82b903a03feacdc"}, + {file = "aiohttp-3.11.18-cp39-cp39-win_amd64.whl", hash = "sha256:5e7007b8d1d09bce37b54111f593d173691c530b80f27c6493b928dabed9e6ef"}, + {file = "aiohttp-3.11.18.tar.gz", hash = "sha256:ae856e1138612b7e412db63b7708735cff4d38d0399f6a5435d3dac2669f558a"}, ] [package.dependencies] @@ -114,7 +112,7 @@ propcache = ">=0.2.0" yarl = ">=1.17.0,<2.0" [package.extras] -speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.2.0) ; sys_platform == \"linux\" or sys_platform == \"darwin\"", "brotlicffi ; platform_python_implementation != \"CPython\""] +speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] [[package]] name = "aiosignal" @@ -122,7 +120,6 @@ version = "1.3.2" description = "aiosignal: a list of registered asynchronous callbacks" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, @@ -137,8 +134,6 @@ version = "5.0.1" description = "Timeout context manager for asyncio programs" optional = false python-versions = ">=3.8" -groups = ["main"] -markers = "python_version < \"3.11\"" files = [ {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, @@ -146,23 +141,22 @@ files = [ [[package]] name = "attrs" -version = "24.3.0" +version = "25.3.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ - {file = "attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308"}, - {file = "attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff"}, + {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, + {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, ] [package.extras] -benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] [[package]] name = "backports-tarfile" @@ -170,8 +164,6 @@ version = "1.2.0" description = "Backport of CPython tarfile module" optional = false python-versions = ">=3.8" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\"" files = [ {file = "backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34"}, {file = "backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991"}, @@ -187,7 +179,6 @@ version = "25.1.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32"}, {file = "black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da"}, @@ -230,14 +221,13 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" -version = "2024.12.14" +version = "2025.4.26" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" -groups = ["main", "dev"] files = [ - {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, - {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, + {file = "certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3"}, + {file = "certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6"}, ] [[package]] @@ -246,8 +236,6 @@ version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\"" files = [ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, @@ -323,104 +311,103 @@ pycparser = "*" [[package]] name = "charset-normalizer" -version = "3.4.1" +version = "3.4.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" -groups = ["main", "dev"] files = [ - {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, - {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, - {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cad5f45b3146325bb38d6855642f6fd609c3f7cad4dbaf75549bf3b904d3184"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2680962a4848b3c4f155dc2ee64505a9c57186d0d56b43123b17ca3de18f0fa"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:36b31da18b8890a76ec181c3cf44326bf2c48e36d393ca1b72b3f484113ea344"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4074c5a429281bf056ddd4c5d3b740ebca4d43ffffe2ef4bf4d2d05114299da"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9e36a97bee9b86ef9a1cf7bb96747eb7a15c2f22bdb5b516434b00f2a599f02"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:1b1bde144d98e446b056ef98e59c256e9294f6b74d7af6846bf5ffdafd687a7d"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:915f3849a011c1f593ab99092f3cecfcb4d65d8feb4a64cf1bf2d22074dc0ec4"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:fb707f3e15060adf5b7ada797624a6c6e0138e2a26baa089df64c68ee98e040f"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:25a23ea5c7edc53e0f29bae2c44fcb5a1aa10591aae107f2a2b2583a9c5cbc64"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:770cab594ecf99ae64c236bc9ee3439c3f46be49796e265ce0cc8bc17b10294f"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-win32.whl", hash = "sha256:6a0289e4589e8bdfef02a80478f1dfcb14f0ab696b5a00e1f4b8a14a307a3c58"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6fc1f5b51fa4cecaa18f2bd7a003f3dd039dd615cd69a2afd6d3b19aed6775f2"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-win32.whl", hash = "sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e"}, + {file = "charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0"}, + {file = "charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63"}, ] [[package]] @@ -429,7 +416,6 @@ version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" -groups = ["dev"] files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -444,8 +430,6 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["dev"] -markers = "platform_system == \"Windows\" or sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -457,8 +441,6 @@ version = "43.0.3" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" files = [ {file = "cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e"}, {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e"}, @@ -508,7 +490,6 @@ version = "0.21.2" description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, @@ -516,17 +497,18 @@ files = [ [[package]] name = "exceptiongroup" -version = "1.2.2" +version = "1.3.0" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" -groups = ["dev"] -markers = "python_version < \"3.11\"" files = [ - {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, - {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, + {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, + {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, ] +[package.dependencies] +typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} + [package.extras] test = ["pytest (>=6)"] @@ -536,7 +518,6 @@ version = "7.2.0" description = "the modular source code checker: pep8 pyflakes and co" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "flake8-7.2.0-py2.py3-none-any.whl", hash = "sha256:93b92ba5bdb60754a6da14fa3b93a9361fd00a59632ada61fd7b130436c40343"}, {file = "flake8-7.2.0.tar.gz", hash = "sha256:fa558ae3f6f7dbf2b4f22663e5343b6b6023620461f8d4ff2019ef4b5ee70426"}, @@ -549,104 +530,115 @@ pyflakes = ">=3.3.0,<3.4.0" [[package]] name = "frozenlist" -version = "1.5.0" +version = "1.6.0" description = "A list-like structure which implements collections.abc.MutableSequence" optional = false -python-versions = ">=3.8" -groups = ["main"] +python-versions = ">=3.9" files = [ - {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a"}, - {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb"}, - {file = "frozenlist-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15538c0cbf0e4fa11d1e3a71f823524b0c46299aed6e10ebb4c2089abd8c3bec"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e79225373c317ff1e35f210dd5f1344ff31066ba8067c307ab60254cd3a78ad5"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9272fa73ca71266702c4c3e2d4a28553ea03418e591e377a03b8e3659d94fa76"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:498524025a5b8ba81695761d78c8dd7382ac0b052f34e66939c42df860b8ff17"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92b5278ed9d50fe610185ecd23c55d8b307d75ca18e94c0e7de328089ac5dcba"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f3c8c1dacd037df16e85227bac13cca58c30da836c6f936ba1df0c05d046d8d"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2ac49a9bedb996086057b75bf93538240538c6d9b38e57c82d51f75a73409d2"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e66cc454f97053b79c2ab09c17fbe3c825ea6b4de20baf1be28919460dd7877f"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3ba5f9a0dfed20337d3e966dc359784c9f96503674c2faf015f7fe8e96798c"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6321899477db90bdeb9299ac3627a6a53c7399c8cd58d25da094007402b039ab"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76e4753701248476e6286f2ef492af900ea67d9706a0155335a40ea21bf3b2f5"}, - {file = "frozenlist-1.5.0-cp310-cp310-win32.whl", hash = "sha256:977701c081c0241d0955c9586ffdd9ce44f7a7795df39b9151cd9a6fd0ce4cfb"}, - {file = "frozenlist-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:189f03b53e64144f90990d29a27ec4f7997d91ed3d01b51fa39d2dbe77540fd4"}, - {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fd74520371c3c4175142d02a976aee0b4cb4a7cc912a60586ffd8d5929979b30"}, - {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f3f7a0fbc219fb4455264cae4d9f01ad41ae6ee8524500f381de64ffaa077d5"}, - {file = "frozenlist-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f47c9c9028f55a04ac254346e92977bf0f166c483c74b4232bee19a6697e4778"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0996c66760924da6e88922756d99b47512a71cfd45215f3570bf1e0b694c206a"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2fe128eb4edeabe11896cb6af88fca5346059f6c8d807e3b910069f39157869"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a8ea951bbb6cacd492e3948b8da8c502a3f814f5d20935aae74b5df2b19cf3d"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de537c11e4aa01d37db0d403b57bd6f0546e71a82347a97c6a9f0dcc532b3a45"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c2623347b933fcb9095841f1cc5d4ff0b278addd743e0e966cb3d460278840d"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cee6798eaf8b1416ef6909b06f7dc04b60755206bddc599f52232606e18179d3"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f5f9da7f5dbc00a604fe74aa02ae7c98bcede8a3b8b9666f9f86fc13993bc71a"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:90646abbc7a5d5c7c19461d2e3eeb76eb0b204919e6ece342feb6032c9325ae9"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bdac3c7d9b705d253b2ce370fde941836a5f8b3c5c2b8fd70940a3ea3af7f4f2"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03d33c2ddbc1816237a67f66336616416e2bbb6beb306e5f890f2eb22b959cdf"}, - {file = "frozenlist-1.5.0-cp311-cp311-win32.whl", hash = "sha256:237f6b23ee0f44066219dae14c70ae38a63f0440ce6750f868ee08775073f942"}, - {file = "frozenlist-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:0cc974cc93d32c42e7b0f6cf242a6bd941c57c61b618e78b6c0a96cb72788c1d"}, - {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21"}, - {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d"}, - {file = "frozenlist-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f"}, - {file = "frozenlist-1.5.0-cp312-cp312-win32.whl", hash = "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8"}, - {file = "frozenlist-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f"}, - {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953"}, - {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0"}, - {file = "frozenlist-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03"}, - {file = "frozenlist-1.5.0-cp313-cp313-win32.whl", hash = "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c"}, - {file = "frozenlist-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28"}, - {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:dd94994fc91a6177bfaafd7d9fd951bc8689b0a98168aa26b5f543868548d3ca"}, - {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0da8bbec082bf6bf18345b180958775363588678f64998c2b7609e34719b10"}, - {file = "frozenlist-1.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73f2e31ea8dd7df61a359b731716018c2be196e5bb3b74ddba107f694fbd7604"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:828afae9f17e6de596825cf4228ff28fbdf6065974e5ac1410cecc22f699d2b3"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1577515d35ed5649d52ab4319db757bb881ce3b2b796d7283e6634d99ace307"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2150cc6305a2c2ab33299453e2968611dacb970d2283a14955923062c8d00b10"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a72b7a6e3cd2725eff67cd64c8f13335ee18fc3c7befc05aed043d24c7b9ccb9"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c16d2fa63e0800723139137d667e1056bee1a1cf7965153d2d104b62855e9b99"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:17dcc32fc7bda7ce5875435003220a457bcfa34ab7924a49a1c19f55b6ee185c"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:97160e245ea33d8609cd2b8fd997c850b56db147a304a262abc2b3be021a9171"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f1e6540b7fa044eee0bb5111ada694cf3dc15f2b0347ca125ee9ca984d5e9e6e"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:91d6c171862df0a6c61479d9724f22efb6109111017c87567cfeb7b5d1449fdf"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c1fac3e2ace2eb1052e9f7c7db480818371134410e1f5c55d65e8f3ac6d1407e"}, - {file = "frozenlist-1.5.0-cp38-cp38-win32.whl", hash = "sha256:b97f7b575ab4a8af9b7bc1d2ef7f29d3afee2226bd03ca3875c16451ad5a7723"}, - {file = "frozenlist-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:374ca2dabdccad8e2a76d40b1d037f5bd16824933bf7bcea3e59c891fd4a0923"}, - {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9bbcdfaf4af7ce002694a4e10a0159d5a8d20056a12b05b45cea944a4953f972"}, - {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1893f948bf6681733aaccf36c5232c231e3b5166d607c5fa77773611df6dc336"}, - {file = "frozenlist-1.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2b5e23253bb709ef57a8e95e6ae48daa9ac5f265637529e4ce6b003a37b2621f"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f253985bb515ecd89629db13cb58d702035ecd8cfbca7d7a7e29a0e6d39af5f"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04a5c6babd5e8fb7d3c871dc8b321166b80e41b637c31a995ed844a6139942b6"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fe0f1c29ba24ba6ff6abf688cb0b7cf1efab6b6aa6adc55441773c252f7411"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:226d72559fa19babe2ccd920273e767c96a49b9d3d38badd7c91a0fdeda8ea08"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15b731db116ab3aedec558573c1a5eec78822b32292fe4f2f0345b7f697745c2"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:366d8f93e3edfe5a918c874702f78faac300209a4d5bf38352b2c1bdc07a766d"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1b96af8c582b94d381a1c1f51ffaedeb77c821c690ea5f01da3d70a487dd0a9b"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c03eff4a41bd4e38415cbed054bbaff4a075b093e2394b6915dca34a40d1e38b"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:50cf5e7ee9b98f22bdecbabf3800ae78ddcc26e4a435515fc72d97903e8488e0"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e76bfbc72353269c44e0bc2cfe171900fbf7f722ad74c9a7b638052afe6a00c"}, - {file = "frozenlist-1.5.0-cp39-cp39-win32.whl", hash = "sha256:666534d15ba8f0fda3f53969117383d5dc021266b3c1a42c9ec4855e4b58b9d3"}, - {file = "frozenlist-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:5c28f4b5dbef8a0d8aad0d4de24d1e9e981728628afaf4ea0792f5d0939372f0"}, - {file = "frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3"}, - {file = "frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817"}, + {file = "frozenlist-1.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e6e558ea1e47fd6fa8ac9ccdad403e5dd5ecc6ed8dda94343056fa4277d5c65e"}, + {file = "frozenlist-1.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f4b3cd7334a4bbc0c472164f3744562cb72d05002cc6fcf58adb104630bbc352"}, + {file = "frozenlist-1.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9799257237d0479736e2b4c01ff26b5c7f7694ac9692a426cb717f3dc02fff9b"}, + {file = "frozenlist-1.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3a7bb0fe1f7a70fb5c6f497dc32619db7d2cdd53164af30ade2f34673f8b1fc"}, + {file = "frozenlist-1.6.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:36d2fc099229f1e4237f563b2a3e0ff7ccebc3999f729067ce4e64a97a7f2869"}, + {file = "frozenlist-1.6.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f27a9f9a86dcf00708be82359db8de86b80d029814e6693259befe82bb58a106"}, + {file = "frozenlist-1.6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75ecee69073312951244f11b8627e3700ec2bfe07ed24e3a685a5979f0412d24"}, + {file = "frozenlist-1.6.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2c7d5aa19714b1b01a0f515d078a629e445e667b9da869a3cd0e6fe7dec78bd"}, + {file = "frozenlist-1.6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69bbd454f0fb23b51cadc9bdba616c9678e4114b6f9fa372d462ff2ed9323ec8"}, + {file = "frozenlist-1.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7daa508e75613809c7a57136dec4871a21bca3080b3a8fc347c50b187df4f00c"}, + {file = "frozenlist-1.6.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:89ffdb799154fd4d7b85c56d5fa9d9ad48946619e0eb95755723fffa11022d75"}, + {file = "frozenlist-1.6.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:920b6bd77d209931e4c263223381d63f76828bec574440f29eb497cf3394c249"}, + {file = "frozenlist-1.6.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d3ceb265249fb401702fce3792e6b44c1166b9319737d21495d3611028d95769"}, + {file = "frozenlist-1.6.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:52021b528f1571f98a7d4258c58aa8d4b1a96d4f01d00d51f1089f2e0323cb02"}, + {file = "frozenlist-1.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0f2ca7810b809ed0f1917293050163c7654cefc57a49f337d5cd9de717b8fad3"}, + {file = "frozenlist-1.6.0-cp310-cp310-win32.whl", hash = "sha256:0e6f8653acb82e15e5443dba415fb62a8732b68fe09936bb6d388c725b57f812"}, + {file = "frozenlist-1.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:f1a39819a5a3e84304cd286e3dc62a549fe60985415851b3337b6f5cc91907f1"}, + {file = "frozenlist-1.6.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ae8337990e7a45683548ffb2fee1af2f1ed08169284cd829cdd9a7fa7470530d"}, + {file = "frozenlist-1.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8c952f69dd524558694818a461855f35d36cc7f5c0adddce37e962c85d06eac0"}, + {file = "frozenlist-1.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8f5fef13136c4e2dee91bfb9a44e236fff78fc2cd9f838eddfc470c3d7d90afe"}, + {file = "frozenlist-1.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:716bbba09611b4663ecbb7cd022f640759af8259e12a6ca939c0a6acd49eedba"}, + {file = "frozenlist-1.6.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7b8c4dc422c1a3ffc550b465090e53b0bf4839047f3e436a34172ac67c45d595"}, + {file = "frozenlist-1.6.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b11534872256e1666116f6587a1592ef395a98b54476addb5e8d352925cb5d4a"}, + {file = "frozenlist-1.6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c6eceb88aaf7221f75be6ab498dc622a151f5f88d536661af3ffc486245a626"}, + {file = "frozenlist-1.6.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62c828a5b195570eb4b37369fcbbd58e96c905768d53a44d13044355647838ff"}, + {file = "frozenlist-1.6.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1c6bd2c6399920c9622362ce95a7d74e7f9af9bfec05fff91b8ce4b9647845a"}, + {file = "frozenlist-1.6.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:49ba23817781e22fcbd45fd9ff2b9b8cdb7b16a42a4851ab8025cae7b22e96d0"}, + {file = "frozenlist-1.6.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:431ef6937ae0f853143e2ca67d6da76c083e8b1fe3df0e96f3802fd37626e606"}, + {file = "frozenlist-1.6.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9d124b38b3c299ca68433597ee26b7819209cb8a3a9ea761dfe9db3a04bba584"}, + {file = "frozenlist-1.6.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:118e97556306402e2b010da1ef21ea70cb6d6122e580da64c056b96f524fbd6a"}, + {file = "frozenlist-1.6.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fb3b309f1d4086b5533cf7bbcf3f956f0ae6469664522f1bde4feed26fba60f1"}, + {file = "frozenlist-1.6.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54dece0d21dce4fdb188a1ffc555926adf1d1c516e493c2914d7c370e454bc9e"}, + {file = "frozenlist-1.6.0-cp311-cp311-win32.whl", hash = "sha256:654e4ba1d0b2154ca2f096bed27461cf6160bc7f504a7f9a9ef447c293caf860"}, + {file = "frozenlist-1.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:3e911391bffdb806001002c1f860787542f45916c3baf764264a52765d5a5603"}, + {file = "frozenlist-1.6.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c5b9e42ace7d95bf41e19b87cec8f262c41d3510d8ad7514ab3862ea2197bfb1"}, + {file = "frozenlist-1.6.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ca9973735ce9f770d24d5484dcb42f68f135351c2fc81a7a9369e48cf2998a29"}, + {file = "frozenlist-1.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6ac40ec76041c67b928ca8aaffba15c2b2ee3f5ae8d0cb0617b5e63ec119ca25"}, + {file = "frozenlist-1.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95b7a8a3180dfb280eb044fdec562f9b461614c0ef21669aea6f1d3dac6ee576"}, + {file = "frozenlist-1.6.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c444d824e22da6c9291886d80c7d00c444981a72686e2b59d38b285617cb52c8"}, + {file = "frozenlist-1.6.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb52c8166499a8150bfd38478248572c924c003cbb45fe3bcd348e5ac7c000f9"}, + {file = "frozenlist-1.6.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b35298b2db9c2468106278537ee529719228950a5fdda686582f68f247d1dc6e"}, + {file = "frozenlist-1.6.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d108e2d070034f9d57210f22fefd22ea0d04609fc97c5f7f5a686b3471028590"}, + {file = "frozenlist-1.6.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e1be9111cb6756868ac242b3c2bd1f09d9aea09846e4f5c23715e7afb647103"}, + {file = "frozenlist-1.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:94bb451c664415f02f07eef4ece976a2c65dcbab9c2f1705b7031a3a75349d8c"}, + {file = "frozenlist-1.6.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:d1a686d0b0949182b8faddea596f3fc11f44768d1f74d4cad70213b2e139d821"}, + {file = "frozenlist-1.6.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ea8e59105d802c5a38bdbe7362822c522230b3faba2aa35c0fa1765239b7dd70"}, + {file = "frozenlist-1.6.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:abc4e880a9b920bc5020bf6a431a6bb40589d9bca3975c980495f63632e8382f"}, + {file = "frozenlist-1.6.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9a79713adfe28830f27a3c62f6b5406c37376c892b05ae070906f07ae4487046"}, + {file = "frozenlist-1.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a0318c2068e217a8f5e3b85e35899f5a19e97141a45bb925bb357cfe1daf770"}, + {file = "frozenlist-1.6.0-cp312-cp312-win32.whl", hash = "sha256:853ac025092a24bb3bf09ae87f9127de9fe6e0c345614ac92536577cf956dfcc"}, + {file = "frozenlist-1.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:2bdfe2d7e6c9281c6e55523acd6c2bf77963cb422fdc7d142fb0cb6621b66878"}, + {file = "frozenlist-1.6.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1d7fb014fe0fbfee3efd6a94fc635aeaa68e5e1720fe9e57357f2e2c6e1a647e"}, + {file = "frozenlist-1.6.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01bcaa305a0fdad12745502bfd16a1c75b14558dabae226852f9159364573117"}, + {file = "frozenlist-1.6.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8b314faa3051a6d45da196a2c495e922f987dc848e967d8cfeaee8a0328b1cd4"}, + {file = "frozenlist-1.6.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da62fecac21a3ee10463d153549d8db87549a5e77eefb8c91ac84bb42bb1e4e3"}, + {file = "frozenlist-1.6.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1eb89bf3454e2132e046f9599fbcf0a4483ed43b40f545551a39316d0201cd1"}, + {file = "frozenlist-1.6.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18689b40cb3936acd971f663ccb8e2589c45db5e2c5f07e0ec6207664029a9c"}, + {file = "frozenlist-1.6.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e67ddb0749ed066b1a03fba812e2dcae791dd50e5da03be50b6a14d0c1a9ee45"}, + {file = "frozenlist-1.6.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc5e64626e6682638d6e44398c9baf1d6ce6bc236d40b4b57255c9d3f9761f1f"}, + {file = "frozenlist-1.6.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:437cfd39564744ae32ad5929e55b18ebd88817f9180e4cc05e7d53b75f79ce85"}, + {file = "frozenlist-1.6.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:62dd7df78e74d924952e2feb7357d826af8d2f307557a779d14ddf94d7311be8"}, + {file = "frozenlist-1.6.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a66781d7e4cddcbbcfd64de3d41a61d6bdde370fc2e38623f30b2bd539e84a9f"}, + {file = "frozenlist-1.6.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:482fe06e9a3fffbcd41950f9d890034b4a54395c60b5e61fae875d37a699813f"}, + {file = "frozenlist-1.6.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e4f9373c500dfc02feea39f7a56e4f543e670212102cc2eeb51d3a99c7ffbde6"}, + {file = "frozenlist-1.6.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e69bb81de06827147b7bfbaeb284d85219fa92d9f097e32cc73675f279d70188"}, + {file = "frozenlist-1.6.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7613d9977d2ab4a9141dde4a149f4357e4065949674c5649f920fec86ecb393e"}, + {file = "frozenlist-1.6.0-cp313-cp313-win32.whl", hash = "sha256:4def87ef6d90429f777c9d9de3961679abf938cb6b7b63d4a7eb8a268babfce4"}, + {file = "frozenlist-1.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:37a8a52c3dfff01515e9bbbee0e6063181362f9de3db2ccf9bc96189b557cbfd"}, + {file = "frozenlist-1.6.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:46138f5a0773d064ff663d273b309b696293d7a7c00a0994c5c13a5078134b64"}, + {file = "frozenlist-1.6.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:f88bc0a2b9c2a835cb888b32246c27cdab5740059fb3688852bf91e915399b91"}, + {file = "frozenlist-1.6.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:777704c1d7655b802c7850255639672e90e81ad6fa42b99ce5ed3fbf45e338dd"}, + {file = "frozenlist-1.6.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85ef8d41764c7de0dcdaf64f733a27352248493a85a80661f3c678acd27e31f2"}, + {file = "frozenlist-1.6.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:da5cb36623f2b846fb25009d9d9215322318ff1c63403075f812b3b2876c8506"}, + {file = "frozenlist-1.6.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cbb56587a16cf0fb8acd19e90ff9924979ac1431baea8681712716a8337577b0"}, + {file = "frozenlist-1.6.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6154c3ba59cda3f954c6333025369e42c3acd0c6e8b6ce31eb5c5b8116c07e0"}, + {file = "frozenlist-1.6.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e8246877afa3f1ae5c979fe85f567d220f86a50dc6c493b9b7d8191181ae01e"}, + {file = "frozenlist-1.6.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b0f6cce16306d2e117cf9db71ab3a9e8878a28176aeaf0dbe35248d97b28d0c"}, + {file = "frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1b8e8cd8032ba266f91136d7105706ad57770f3522eac4a111d77ac126a25a9b"}, + {file = "frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:e2ada1d8515d3ea5378c018a5f6d14b4994d4036591a52ceaf1a1549dec8e1ad"}, + {file = "frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:cdb2c7f071e4026c19a3e32b93a09e59b12000751fc9b0b7758da899e657d215"}, + {file = "frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:03572933a1969a6d6ab509d509e5af82ef80d4a5d4e1e9f2e1cdd22c77a3f4d2"}, + {file = "frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:77effc978947548b676c54bbd6a08992759ea6f410d4987d69feea9cd0919911"}, + {file = "frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a2bda8be77660ad4089caf2223fdbd6db1858462c4b85b67fbfa22102021e497"}, + {file = "frozenlist-1.6.0-cp313-cp313t-win32.whl", hash = "sha256:a4d96dc5bcdbd834ec6b0f91027817214216b5b30316494d2b1aebffb87c534f"}, + {file = "frozenlist-1.6.0-cp313-cp313t-win_amd64.whl", hash = "sha256:e18036cb4caa17ea151fd5f3d70be9d354c99eb8cf817a3ccde8a7873b074348"}, + {file = "frozenlist-1.6.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:536a1236065c29980c15c7229fbb830dedf809708c10e159b8136534233545f0"}, + {file = "frozenlist-1.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ed5e3a4462ff25ca84fb09e0fada8ea267df98a450340ead4c91b44857267d70"}, + {file = "frozenlist-1.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e19c0fc9f4f030fcae43b4cdec9e8ab83ffe30ec10c79a4a43a04d1af6c5e1ad"}, + {file = "frozenlist-1.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c608f833897501dac548585312d73a7dca028bf3b8688f0d712b7acfaf7fb3"}, + {file = "frozenlist-1.6.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0dbae96c225d584f834b8d3cc688825911960f003a85cb0fd20b6e5512468c42"}, + {file = "frozenlist-1.6.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:625170a91dd7261a1d1c2a0c1a353c9e55d21cd67d0852185a5fef86587e6f5f"}, + {file = "frozenlist-1.6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1db8b2fc7ee8a940b547a14c10e56560ad3ea6499dc6875c354e2335812f739d"}, + {file = "frozenlist-1.6.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4da6fc43048b648275a220e3a61c33b7fff65d11bdd6dcb9d9c145ff708b804c"}, + {file = "frozenlist-1.6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef8e7e8f2f3820c5f175d70fdd199b79e417acf6c72c5d0aa8f63c9f721646f"}, + {file = "frozenlist-1.6.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:aa733d123cc78245e9bb15f29b44ed9e5780dc6867cfc4e544717b91f980af3b"}, + {file = "frozenlist-1.6.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:ba7f8d97152b61f22d7f59491a781ba9b177dd9f318486c5fbc52cde2db12189"}, + {file = "frozenlist-1.6.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:56a0b8dd6d0d3d971c91f1df75e824986667ccce91e20dca2023683814344791"}, + {file = "frozenlist-1.6.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:5c9e89bf19ca148efcc9e3c44fd4c09d5af85c8a7dd3dbd0da1cb83425ef4983"}, + {file = "frozenlist-1.6.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:1330f0a4376587face7637dfd245380a57fe21ae8f9d360c1c2ef8746c4195fa"}, + {file = "frozenlist-1.6.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2187248203b59625566cac53572ec8c2647a140ee2738b4e36772930377a533c"}, + {file = "frozenlist-1.6.0-cp39-cp39-win32.whl", hash = "sha256:2b8cf4cfea847d6c12af06091561a89740f1f67f331c3fa8623391905e878530"}, + {file = "frozenlist-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:1255d5d64328c5a0d066ecb0f02034d086537925f1f04b50b1ae60d37afbf572"}, + {file = "frozenlist-1.6.0-py3-none-any.whl", hash = "sha256:535eec9987adb04701266b92745d6cdcef2e77669299359c3009c3404dd5d191"}, + {file = "frozenlist-1.6.0.tar.gz", hash = "sha256:b99655c32c1c8e06d111e7f41c06c29a5318cb1835df23a45518e02a47c63b68"}, ] [[package]] @@ -655,7 +647,6 @@ version = "1.5.0" description = "A tool for generating OIDC identities" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "id-1.5.0-py3-none-any.whl", hash = "sha256:f1434e1cef91f2cbb8a4ec64663d5a23b9ed43ef44c4c957d02583d61714c658"}, {file = "id-1.5.0.tar.gz", hash = "sha256:292cb8a49eacbbdbce97244f47a97b4c62540169c976552e497fd57df0734c1d"}, @@ -675,7 +666,6 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" -groups = ["main", "dev"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -686,39 +676,36 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2 [[package]] name = "importlib-metadata" -version = "8.5.0" +version = "8.7.0" description = "Read metadata from Python packages" optional = false -python-versions = ">=3.8" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version < \"3.10\"" +python-versions = ">=3.9" files = [ - {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, - {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, + {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"}, + {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"}, ] [package.dependencies] zipp = ">=3.20" [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib-resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +test = ["flufl.flake8", "importlib_resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] [[package]] name = "iniconfig" -version = "2.0.0" +version = "2.1.0" description = "brain-dead simple config-ini parsing" optional = false -python-versions = ">=3.7" -groups = ["dev"] +python-versions = ">=3.8" files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, ] [[package]] @@ -727,8 +714,6 @@ version = "3.4.0" description = "Utility functions for Python class constructs" optional = false python-versions = ">=3.8" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" files = [ {file = "jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790"}, {file = "jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd"}, @@ -747,8 +732,6 @@ version = "6.0.1" description = "Useful decorators and context managers" optional = false python-versions = ">=3.8" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" files = [ {file = "jaraco.context-6.0.1-py3-none-any.whl", hash = "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4"}, {file = "jaraco_context-6.0.1.tar.gz", hash = "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3"}, @@ -759,7 +742,7 @@ files = [ [package.extras] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -test = ["portend", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +test = ["portend", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [[package]] name = "jaraco-functools" @@ -767,8 +750,6 @@ version = "4.1.0" description = "Functools like those found in stdlib" optional = false python-versions = ">=3.8" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" files = [ {file = "jaraco.functools-4.1.0-py3-none-any.whl", hash = "sha256:ad159f13428bc4acbf5541ad6dec511f91573b90fba04df61dafa2a1231cf649"}, {file = "jaraco_functools-4.1.0.tar.gz", hash = "sha256:70f7e0e2ae076498e212562325e805204fc092d7b4c17e0e86c959e249701a9d"}, @@ -778,7 +759,7 @@ files = [ more-itertools = "*" [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] @@ -787,20 +768,18 @@ type = ["pytest-mypy"] [[package]] name = "jeepney" -version = "0.8.0" +version = "0.9.0" description = "Low-level, pure Python DBus protocol wrapper." optional = false python-versions = ">=3.7" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" files = [ - {file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"}, - {file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"}, + {file = "jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683"}, + {file = "jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732"}, ] [package.extras] test = ["async-timeout", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"] -trio = ["async_generator ; python_version == \"3.6\"", "trio"] +trio = ["trio"] [[package]] name = "keyring" @@ -808,8 +787,6 @@ version = "25.6.0" description = "Store and access your passwords safely." optional = false python-versions = ">=3.9" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" files = [ {file = "keyring-25.6.0-py3-none-any.whl", hash = "sha256:552a3f7af126ece7ed5c89753650eec89c7eaae8617d0aa4d9ad2b75111266bd"}, {file = "keyring-25.6.0.tar.gz", hash = "sha256:0b39998aa941431eb3d9b0d4b2460bc773b9df6fed7621c2dfb291a7e0187a66"}, @@ -825,7 +802,7 @@ pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""} SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] completion = ["shtab (>=1.1.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] @@ -839,7 +816,6 @@ version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, @@ -864,7 +840,6 @@ version = "0.7.0" description = "McCabe checker, plugin for flake8" optional = false python-versions = ">=3.6" -groups = ["dev"] files = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, @@ -876,7 +851,6 @@ version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" -groups = ["dev"] files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, @@ -884,117 +858,126 @@ files = [ [[package]] name = "more-itertools" -version = "10.5.0" +version = "10.7.0" description = "More routines for operating on iterables, beyond itertools" optional = false -python-versions = ">=3.8" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" +python-versions = ">=3.9" files = [ - {file = "more-itertools-10.5.0.tar.gz", hash = "sha256:5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6"}, - {file = "more_itertools-10.5.0-py3-none-any.whl", hash = "sha256:037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef"}, + {file = "more_itertools-10.7.0-py3-none-any.whl", hash = "sha256:d43980384673cb07d2f7d2d918c616b30c659c089ee23953f601d6609c67510e"}, + {file = "more_itertools-10.7.0.tar.gz", hash = "sha256:9fddd5403be01a94b204faadcff459ec3568cf110265d3c54323e1e866ad29d3"}, ] [[package]] name = "multidict" -version = "6.1.0" +version = "6.4.3" description = "multidict implementation" optional = false -python-versions = ">=3.8" -groups = ["main"] +python-versions = ">=3.9" files = [ - {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, - {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, - {file = "multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7"}, - {file = "multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0"}, - {file = "multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d"}, - {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"}, - {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"}, - {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"}, - {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"}, - {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"}, - {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"}, - {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"}, - {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"}, - {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"}, - {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"}, - {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"}, - {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"}, - {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"}, - {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"}, - {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"}, - {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392"}, - {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a"}, - {file = "multidict-6.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd"}, - {file = "multidict-6.1.0-cp38-cp38-win32.whl", hash = "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167"}, - {file = "multidict-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef"}, - {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c"}, - {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1"}, - {file = "multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43"}, - {file = "multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada"}, - {file = "multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a"}, - {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"}, - {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"}, + {file = "multidict-6.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:32a998bd8a64ca48616eac5a8c1cc4fa38fb244a3facf2eeb14abe186e0f6cc5"}, + {file = "multidict-6.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a54ec568f1fc7f3c313c2f3b16e5db346bf3660e1309746e7fccbbfded856188"}, + {file = "multidict-6.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a7be07e5df178430621c716a63151165684d3e9958f2bbfcb644246162007ab7"}, + {file = "multidict-6.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b128dbf1c939674a50dd0b28f12c244d90e5015e751a4f339a96c54f7275e291"}, + {file = "multidict-6.4.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b9cb19dfd83d35b6ff24a4022376ea6e45a2beba8ef3f0836b8a4b288b6ad685"}, + {file = "multidict-6.4.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3cf62f8e447ea2c1395afa289b332e49e13d07435369b6f4e41f887db65b40bf"}, + {file = "multidict-6.4.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:909f7d43ff8f13d1adccb6a397094adc369d4da794407f8dd592c51cf0eae4b1"}, + {file = "multidict-6.4.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0bb8f8302fbc7122033df959e25777b0b7659b1fd6bcb9cb6bed76b5de67afef"}, + {file = "multidict-6.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:224b79471b4f21169ea25ebc37ed6f058040c578e50ade532e2066562597b8a9"}, + {file = "multidict-6.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a7bd27f7ab3204f16967a6f899b3e8e9eb3362c0ab91f2ee659e0345445e0078"}, + {file = "multidict-6.4.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:99592bd3162e9c664671fd14e578a33bfdba487ea64bcb41d281286d3c870ad7"}, + {file = "multidict-6.4.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a62d78a1c9072949018cdb05d3c533924ef8ac9bcb06cbf96f6d14772c5cd451"}, + {file = "multidict-6.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3ccdde001578347e877ca4f629450973c510e88e8865d5aefbcb89b852ccc666"}, + {file = "multidict-6.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:eccb67b0e78aa2e38a04c5ecc13bab325a43e5159a181a9d1a6723db913cbb3c"}, + {file = "multidict-6.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8b6fcf6054fc4114a27aa865f8840ef3d675f9316e81868e0ad5866184a6cba5"}, + {file = "multidict-6.4.3-cp310-cp310-win32.whl", hash = "sha256:f92c7f62d59373cd93bc9969d2da9b4b21f78283b1379ba012f7ee8127b3152e"}, + {file = "multidict-6.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:b57e28dbc031d13916b946719f213c494a517b442d7b48b29443e79610acd887"}, + {file = "multidict-6.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f6f19170197cc29baccd33ccc5b5d6a331058796485857cf34f7635aa25fb0cd"}, + {file = "multidict-6.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f2882bf27037eb687e49591690e5d491e677272964f9ec7bc2abbe09108bdfb8"}, + {file = "multidict-6.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fbf226ac85f7d6b6b9ba77db4ec0704fde88463dc17717aec78ec3c8546c70ad"}, + {file = "multidict-6.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e329114f82ad4b9dd291bef614ea8971ec119ecd0f54795109976de75c9a852"}, + {file = "multidict-6.4.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1f4e0334d7a555c63f5c8952c57ab6f1c7b4f8c7f3442df689fc9f03df315c08"}, + {file = "multidict-6.4.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:740915eb776617b57142ce0bb13b7596933496e2f798d3d15a20614adf30d229"}, + {file = "multidict-6.4.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255dac25134d2b141c944b59a0d2f7211ca12a6d4779f7586a98b4b03ea80508"}, + {file = "multidict-6.4.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4e8535bd4d741039b5aad4285ecd9b902ef9e224711f0b6afda6e38d7ac02c7"}, + {file = "multidict-6.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c433a33be000dd968f5750722eaa0991037be0be4a9d453eba121774985bc8"}, + {file = "multidict-6.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4eb33b0bdc50acd538f45041f5f19945a1f32b909b76d7b117c0c25d8063df56"}, + {file = "multidict-6.4.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:75482f43465edefd8a5d72724887ccdcd0c83778ded8f0cb1e0594bf71736cc0"}, + {file = "multidict-6.4.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce5b3082e86aee80b3925ab4928198450d8e5b6466e11501fe03ad2191c6d777"}, + {file = "multidict-6.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e413152e3212c4d39f82cf83c6f91be44bec9ddea950ce17af87fbf4e32ca6b2"}, + {file = "multidict-6.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:8aac2eeff69b71f229a405c0a4b61b54bade8e10163bc7b44fcd257949620618"}, + {file = "multidict-6.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ab583ac203af1d09034be41458feeab7863c0635c650a16f15771e1386abf2d7"}, + {file = "multidict-6.4.3-cp311-cp311-win32.whl", hash = "sha256:1b2019317726f41e81154df636a897de1bfe9228c3724a433894e44cd2512378"}, + {file = "multidict-6.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:43173924fa93c7486402217fab99b60baf78d33806af299c56133a3755f69589"}, + {file = "multidict-6.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f1c2f58f08b36f8475f3ec6f5aeb95270921d418bf18f90dffd6be5c7b0e676"}, + {file = "multidict-6.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:26ae9ad364fc61b936fb7bf4c9d8bd53f3a5b4417142cd0be5c509d6f767e2f1"}, + {file = "multidict-6.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:659318c6c8a85f6ecfc06b4e57529e5a78dfdd697260cc81f683492ad7e9435a"}, + {file = "multidict-6.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1eb72c741fd24d5a28242ce72bb61bc91f8451877131fa3fe930edb195f7054"}, + {file = "multidict-6.4.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3cd06d88cb7398252284ee75c8db8e680aa0d321451132d0dba12bc995f0adcc"}, + {file = "multidict-6.4.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4543d8dc6470a82fde92b035a92529317191ce993533c3c0c68f56811164ed07"}, + {file = "multidict-6.4.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:30a3ebdc068c27e9d6081fca0e2c33fdf132ecea703a72ea216b81a66860adde"}, + {file = "multidict-6.4.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b038f10e23f277153f86f95c777ba1958bcd5993194fda26a1d06fae98b2f00c"}, + {file = "multidict-6.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c605a2b2dc14282b580454b9b5d14ebe0668381a3a26d0ac39daa0ca115eb2ae"}, + {file = "multidict-6.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8bd2b875f4ca2bb527fe23e318ddd509b7df163407b0fb717df229041c6df5d3"}, + {file = "multidict-6.4.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c2e98c840c9c8e65c0e04b40c6c5066c8632678cd50c8721fdbcd2e09f21a507"}, + {file = "multidict-6.4.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:66eb80dd0ab36dbd559635e62fba3083a48a252633164857a1d1684f14326427"}, + {file = "multidict-6.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c23831bdee0a2a3cf21be057b5e5326292f60472fb6c6f86392bbf0de70ba731"}, + {file = "multidict-6.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1535cec6443bfd80d028052e9d17ba6ff8a5a3534c51d285ba56c18af97e9713"}, + {file = "multidict-6.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3b73e7227681f85d19dec46e5b881827cd354aabe46049e1a61d2f9aaa4e285a"}, + {file = "multidict-6.4.3-cp312-cp312-win32.whl", hash = "sha256:8eac0c49df91b88bf91f818e0a24c1c46f3622978e2c27035bfdca98e0e18124"}, + {file = "multidict-6.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:11990b5c757d956cd1db7cb140be50a63216af32cd6506329c2c59d732d802db"}, + {file = "multidict-6.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a76534263d03ae0cfa721fea40fd2b5b9d17a6f85e98025931d41dc49504474"}, + {file = "multidict-6.4.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:805031c2f599eee62ac579843555ed1ce389ae00c7e9f74c2a1b45e0564a88dd"}, + {file = "multidict-6.4.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c56c179839d5dcf51d565132185409d1d5dd8e614ba501eb79023a6cab25576b"}, + {file = "multidict-6.4.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c64f4ddb3886dd8ab71b68a7431ad4aa01a8fa5be5b11543b29674f29ca0ba3"}, + {file = "multidict-6.4.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3002a856367c0b41cad6784f5b8d3ab008eda194ed7864aaa58f65312e2abcac"}, + {file = "multidict-6.4.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d75e621e7d887d539d6e1d789f0c64271c250276c333480a9e1de089611f790"}, + {file = "multidict-6.4.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:995015cf4a3c0d72cbf453b10a999b92c5629eaf3a0c3e1efb4b5c1f602253bb"}, + {file = "multidict-6.4.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2b0fabae7939d09d7d16a711468c385272fa1b9b7fb0d37e51143585d8e72e0"}, + {file = "multidict-6.4.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:61ed4d82f8a1e67eb9eb04f8587970d78fe7cddb4e4d6230b77eda23d27938f9"}, + {file = "multidict-6.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:062428944a8dc69df9fdc5d5fc6279421e5f9c75a9ee3f586f274ba7b05ab3c8"}, + {file = "multidict-6.4.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:b90e27b4674e6c405ad6c64e515a505c6d113b832df52fdacb6b1ffd1fa9a1d1"}, + {file = "multidict-6.4.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7d50d4abf6729921e9613d98344b74241572b751c6b37feed75fb0c37bd5a817"}, + {file = "multidict-6.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:43fe10524fb0a0514be3954be53258e61d87341008ce4914f8e8b92bee6f875d"}, + {file = "multidict-6.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:236966ca6c472ea4e2d3f02f6673ebfd36ba3f23159c323f5a496869bc8e47c9"}, + {file = "multidict-6.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:422a5ec315018e606473ba1f5431e064cf8b2a7468019233dcf8082fabad64c8"}, + {file = "multidict-6.4.3-cp313-cp313-win32.whl", hash = "sha256:f901a5aace8e8c25d78960dcc24c870c8d356660d3b49b93a78bf38eb682aac3"}, + {file = "multidict-6.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:1c152c49e42277bc9a2f7b78bd5fa10b13e88d1b0328221e7aef89d5c60a99a5"}, + {file = "multidict-6.4.3-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:be8751869e28b9c0d368d94f5afcb4234db66fe8496144547b4b6d6a0645cfc6"}, + {file = "multidict-6.4.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0d4b31f8a68dccbcd2c0ea04f0e014f1defc6b78f0eb8b35f2265e8716a6df0c"}, + {file = "multidict-6.4.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:032efeab3049e37eef2ff91271884303becc9e54d740b492a93b7e7266e23756"}, + {file = "multidict-6.4.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e78006af1a7c8a8007e4f56629d7252668344442f66982368ac06522445e375"}, + {file = "multidict-6.4.3-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:daeac9dd30cda8703c417e4fddccd7c4dc0c73421a0b54a7da2713be125846be"}, + {file = "multidict-6.4.3-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f6f90700881438953eae443a9c6f8a509808bc3b185246992c4233ccee37fea"}, + {file = "multidict-6.4.3-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f84627997008390dd15762128dcf73c3365f4ec0106739cde6c20a07ed198ec8"}, + {file = "multidict-6.4.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3307b48cd156153b117c0ea54890a3bdbf858a5b296ddd40dc3852e5f16e9b02"}, + {file = "multidict-6.4.3-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ead46b0fa1dcf5af503a46e9f1c2e80b5d95c6011526352fa5f42ea201526124"}, + {file = "multidict-6.4.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1748cb2743bedc339d63eb1bca314061568793acd603a6e37b09a326334c9f44"}, + {file = "multidict-6.4.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:acc9fa606f76fc111b4569348cc23a771cb52c61516dcc6bcef46d612edb483b"}, + {file = "multidict-6.4.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:31469d5832b5885adeb70982e531ce86f8c992334edd2f2254a10fa3182ac504"}, + {file = "multidict-6.4.3-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ba46b51b6e51b4ef7bfb84b82f5db0dc5e300fb222a8a13b8cd4111898a869cf"}, + {file = "multidict-6.4.3-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:389cfefb599edf3fcfd5f64c0410da686f90f5f5e2c4d84e14f6797a5a337af4"}, + {file = "multidict-6.4.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:64bc2bbc5fba7b9db5c2c8d750824f41c6994e3882e6d73c903c2afa78d091e4"}, + {file = "multidict-6.4.3-cp313-cp313t-win32.whl", hash = "sha256:0ecdc12ea44bab2807d6b4a7e5eef25109ab1c82a8240d86d3c1fc9f3b72efd5"}, + {file = "multidict-6.4.3-cp313-cp313t-win_amd64.whl", hash = "sha256:7146a8742ea71b5d7d955bffcef58a9e6e04efba704b52a460134fefd10a8208"}, + {file = "multidict-6.4.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5427a2679e95a642b7f8b0f761e660c845c8e6fe3141cddd6b62005bd133fc21"}, + {file = "multidict-6.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:24a8caa26521b9ad09732972927d7b45b66453e6ebd91a3c6a46d811eeb7349b"}, + {file = "multidict-6.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6b5a272bc7c36a2cd1b56ddc6bff02e9ce499f9f14ee4a45c45434ef083f2459"}, + {file = "multidict-6.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edf74dc5e212b8c75165b435c43eb0d5e81b6b300a938a4eb82827119115e840"}, + {file = "multidict-6.4.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9f35de41aec4b323c71f54b0ca461ebf694fb48bec62f65221f52e0017955b39"}, + {file = "multidict-6.4.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae93e0ff43b6f6892999af64097b18561691ffd835e21a8348a441e256592e1f"}, + {file = "multidict-6.4.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e3929269e9d7eff905d6971d8b8c85e7dbc72c18fb99c8eae6fe0a152f2e343"}, + {file = "multidict-6.4.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb6214fe1750adc2a1b801a199d64b5a67671bf76ebf24c730b157846d0e90d2"}, + {file = "multidict-6.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d79cf5c0c6284e90f72123f4a3e4add52d6c6ebb4a9054e88df15b8d08444c6"}, + {file = "multidict-6.4.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2427370f4a255262928cd14533a70d9738dfacadb7563bc3b7f704cc2360fc4e"}, + {file = "multidict-6.4.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:fbd8d737867912b6c5f99f56782b8cb81f978a97b4437a1c476de90a3e41c9a1"}, + {file = "multidict-6.4.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0ee1bf613c448997f73fc4efb4ecebebb1c02268028dd4f11f011f02300cf1e8"}, + {file = "multidict-6.4.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:578568c4ba5f2b8abd956baf8b23790dbfdc953e87d5b110bce343b4a54fc9e7"}, + {file = "multidict-6.4.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a059ad6b80de5b84b9fa02a39400319e62edd39d210b4e4f8c4f1243bdac4752"}, + {file = "multidict-6.4.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:dd53893675b729a965088aaadd6a1f326a72b83742b056c1065bdd2e2a42b4df"}, + {file = "multidict-6.4.3-cp39-cp39-win32.whl", hash = "sha256:abcfed2c4c139f25c2355e180bcc077a7cae91eefbb8b3927bb3f836c9586f1f"}, + {file = "multidict-6.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:b1b389ae17296dd739015d5ddb222ee99fd66adeae910de21ac950e00979d897"}, + {file = "multidict-6.4.3-py3-none-any.whl", hash = "sha256:59fe01ee8e2a1e8ceb3f6dbb216b09c8d9f4ef1c22c4fc825d045a147fa2ebc9"}, + {file = "multidict-6.4.3.tar.gz", hash = "sha256:3ada0b058c9f213c5f95ba301f922d402ac234f1111a7d8fd70f1b99f3c281ec"}, ] [package.dependencies] @@ -1002,48 +985,46 @@ typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""} [[package]] name = "mypy-extensions" -version = "1.0.0" +version = "1.1.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false -python-versions = ">=3.5" -groups = ["dev"] +python-versions = ">=3.8" files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, ] [[package]] name = "nh3" -version = "0.2.20" +version = "0.2.21" description = "Python binding to Ammonia HTML sanitizer Rust crate" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ - {file = "nh3-0.2.20-cp313-cp313t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e1061a4ab6681f6bdf72b110eea0c4e1379d57c9de937db3be4202f7ad6043db"}, - {file = "nh3-0.2.20-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb4254b1dac4a1ee49919a5b3f1caf9803ea8dada1816d9e8289e63d3cd0dd9a"}, - {file = "nh3-0.2.20-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ae9cbd713524cdb81e64663d0d6aae26f678db9f2cd9db0bf162606f1f9f20c"}, - {file = "nh3-0.2.20-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e1f7370b4e14cc03f5ae141ef30a1caf81fa5787711f80be9081418dd9eb79d2"}, - {file = "nh3-0.2.20-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:ac4d27dc836a476efffc6eb661994426b8b805c951b29c9cf2ff36bc9ad58bc5"}, - {file = "nh3-0.2.20-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:4fd2e9248725ebcedac3997a8d3da0d90a12a28c9179c6ba51f1658938ac30d0"}, - {file = "nh3-0.2.20-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f7d564871833ddbe54df3aa59053b1110729d3a800cb7628ae8f42adb3d75208"}, - {file = "nh3-0.2.20-cp313-cp313t-win32.whl", hash = "sha256:d2a176fd4306b6f0f178a3f67fac91bd97a3a8d8fafb771c9b9ef675ba5c8886"}, - {file = "nh3-0.2.20-cp313-cp313t-win_amd64.whl", hash = "sha256:6ed834c68452a600f517dd3e1534dbfaff1f67f98899fecf139a055a25d99150"}, - {file = "nh3-0.2.20-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:76e2f603b30c02ff6456b233a83fc377dedab6a50947b04e960a6b905637b776"}, - {file = "nh3-0.2.20-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:181063c581defe683bd4bb78188ac9936d208aebbc74c7f7c16b6a32ae2ebb38"}, - {file = "nh3-0.2.20-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:231addb7643c952cd6d71f1c8702d703f8fe34afcb20becb3efb319a501a12d7"}, - {file = "nh3-0.2.20-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1b9a8340a0aab991c68a5ca938d35ef4a8a3f4bf1b455da8855a40bee1fa0ace"}, - {file = "nh3-0.2.20-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10317cd96fe4bbd4eb6b95f3920b71c902157ad44fed103fdcde43e3b8ee8be6"}, - {file = "nh3-0.2.20-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8698db4c04b140800d1a1cd3067fda399e36e1e2b8fc1fe04292a907350a3e9b"}, - {file = "nh3-0.2.20-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eb04b9c3deb13c3a375ea39fd4a3c00d1f92e8fb2349f25f1e3e4506751774b"}, - {file = "nh3-0.2.20-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92f3f1c4f47a2c6f3ca7317b1d5ced05bd29556a75d3a4e2715652ae9d15c05d"}, - {file = "nh3-0.2.20-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ddefa9fd6794a87e37d05827d299d4b53a3ec6f23258101907b96029bfef138a"}, - {file = "nh3-0.2.20-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ce3731c8f217685d33d9268362e5b4f770914e922bba94d368ab244a59a6c397"}, - {file = "nh3-0.2.20-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:09f037c02fc2c43b211ff1523de32801dcfb0918648d8e651c36ef890f1731ec"}, - {file = "nh3-0.2.20-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:813f1c8012dd64c990514b795508abb90789334f76a561fa0fd4ca32d2275330"}, - {file = "nh3-0.2.20-cp38-abi3-win32.whl", hash = "sha256:47b2946c0e13057855209daeffb45dc910bd0c55daf10190bb0b4b60e2999784"}, - {file = "nh3-0.2.20-cp38-abi3-win_amd64.whl", hash = "sha256:da87573f03084edae8eb87cfe811ec338606288f81d333c07d2a9a0b9b976c0b"}, - {file = "nh3-0.2.20.tar.gz", hash = "sha256:9705c42d7ff88a0bea546c82d7fe5e59135e3d3f057e485394f491248a1f8ed5"}, + {file = "nh3-0.2.21-cp313-cp313t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:fcff321bd60c6c5c9cb4ddf2554e22772bb41ebd93ad88171bbbb6f271255286"}, + {file = "nh3-0.2.21-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31eedcd7d08b0eae28ba47f43fd33a653b4cdb271d64f1aeda47001618348fde"}, + {file = "nh3-0.2.21-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d426d7be1a2f3d896950fe263332ed1662f6c78525b4520c8e9861f8d7f0d243"}, + {file = "nh3-0.2.21-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9d67709bc0d7d1f5797b21db26e7a8b3d15d21c9c5f58ccfe48b5328483b685b"}, + {file = "nh3-0.2.21-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:55823c5ea1f6b267a4fad5de39bc0524d49a47783e1fe094bcf9c537a37df251"}, + {file = "nh3-0.2.21-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:818f2b6df3763e058efa9e69677b5a92f9bc0acff3295af5ed013da544250d5b"}, + {file = "nh3-0.2.21-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b3b5c58161e08549904ac4abd450dacd94ff648916f7c376ae4b2c0652b98ff9"}, + {file = "nh3-0.2.21-cp313-cp313t-win32.whl", hash = "sha256:637d4a10c834e1b7d9548592c7aad760611415fcd5bd346f77fd8a064309ae6d"}, + {file = "nh3-0.2.21-cp313-cp313t-win_amd64.whl", hash = "sha256:713d16686596e556b65e7f8c58328c2df63f1a7abe1277d87625dcbbc012ef82"}, + {file = "nh3-0.2.21-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:a772dec5b7b7325780922dd904709f0f5f3a79fbf756de5291c01370f6df0967"}, + {file = "nh3-0.2.21-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d002b648592bf3033adfd875a48f09b8ecc000abd7f6a8769ed86b6ccc70c759"}, + {file = "nh3-0.2.21-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2a5174551f95f2836f2ad6a8074560f261cf9740a48437d6151fd2d4d7d617ab"}, + {file = "nh3-0.2.21-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b8d55ea1fc7ae3633d758a92aafa3505cd3cc5a6e40470c9164d54dff6f96d42"}, + {file = "nh3-0.2.21-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6ae319f17cd8960d0612f0f0ddff5a90700fa71926ca800e9028e7851ce44a6f"}, + {file = "nh3-0.2.21-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63ca02ac6f27fc80f9894409eb61de2cb20ef0a23740c7e29f9ec827139fa578"}, + {file = "nh3-0.2.21-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5f77e62aed5c4acad635239ac1290404c7e940c81abe561fd2af011ff59f585"}, + {file = "nh3-0.2.21-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:087ffadfdcd497658c3adc797258ce0f06be8a537786a7217649fc1c0c60c293"}, + {file = "nh3-0.2.21-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ac7006c3abd097790e611fe4646ecb19a8d7f2184b882f6093293b8d9b887431"}, + {file = "nh3-0.2.21-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:6141caabe00bbddc869665b35fc56a478eb774a8c1dfd6fba9fe1dfdf29e6efa"}, + {file = "nh3-0.2.21-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:20979783526641c81d2f5bfa6ca5ccca3d1e4472474b162c6256745fbfe31cd1"}, + {file = "nh3-0.2.21-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a7ea28cd49293749d67e4fcf326c554c83ec912cd09cd94aa7ec3ab1921c8283"}, + {file = "nh3-0.2.21-cp38-abi3-win32.whl", hash = "sha256:6c9c30b8b0d291a7c5ab0967ab200598ba33208f754f2f4920e9343bdd88f79a"}, + {file = "nh3-0.2.21-cp38-abi3-win_amd64.whl", hash = "sha256:bb0014948f04d7976aabae43fcd4cb7f551f9f8ce785a4c9ef66e6c2590f8629"}, + {file = "nh3-0.2.21.tar.gz", hash = "sha256:4990e7ee6a55490dbf00d61a6f476c9a3258e31e711e13713b2ea7d6616f670e"}, ] [[package]] @@ -1052,7 +1033,6 @@ version = "1.4.2" description = "A client library for OAuth2" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "oauth2-client-1.4.2.tar.gz", hash = "sha256:5381900448ff1ae762eb7c65c501002eac46bb5ca2f49477fdfeaf9e9969f284"}, {file = "oauth2_client-1.4.2-py3-none-any.whl", hash = "sha256:7b938ba8166128a3c4c15ad23ca0c95a2468f8e8b6069d019ebc73360c15c7ca"}, @@ -1063,14 +1043,13 @@ requests = ">=2.5.0" [[package]] name = "packaging" -version = "24.2" +version = "25.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ - {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, - {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, ] [[package]] @@ -1079,7 +1058,6 @@ version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -1087,36 +1065,34 @@ files = [ [[package]] name = "platformdirs" -version = "4.3.6" +version = "4.3.8" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false -python-versions = ">=3.8" -groups = ["dev"] +python-versions = ">=3.9" files = [ - {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, - {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, + {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, + {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, ] [package.extras] -docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] -type = ["mypy (>=1.11.2)"] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.14.1)"] [[package]] name = "pluggy" -version = "1.5.0" +version = "1.6.0" description = "plugin and hook calling mechanisms for python" optional = false -python-versions = ">=3.8" -groups = ["dev"] +python-versions = ">=3.9" files = [ - {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, - {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, ] [package.extras] dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] +testing = ["coverage", "pytest", "pytest-benchmark"] [[package]] name = "polling2" @@ -1124,7 +1100,6 @@ version = "0.5.0" description = "Updated polling utility with many configurable options" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "polling2-0.5.0-py2.py3-none-any.whl", hash = "sha256:ad86d56fbd7502f0856cac2d0109d595c18fa6c7fb12c88cee5e5d16c17286c1"}, {file = "polling2-0.5.0.tar.gz", hash = "sha256:90b7da82cf7adbb48029724d3546af93f21ab6e592ec37c8c4619aedd010e342"}, @@ -1132,115 +1107,127 @@ files = [ [[package]] name = "propcache" -version = "0.2.1" +version = "0.3.1" description = "Accelerated property cache" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ - {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6"}, - {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2"}, - {file = "propcache-0.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6445804cf4ec763dc70de65a3b0d9954e868609e83850a47ca4f0cb64bd79fea"}, - {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9479aa06a793c5aeba49ce5c5692ffb51fcd9a7016e017d555d5e2b0045d212"}, - {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9631c5e8b5b3a0fda99cb0d29c18133bca1e18aea9effe55adb3da1adef80d3"}, - {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3156628250f46a0895f1f36e1d4fbe062a1af8718ec3ebeb746f1d23f0c5dc4d"}, - {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b6fb63ae352e13748289f04f37868099e69dba4c2b3e271c46061e82c745634"}, - {file = "propcache-0.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:887d9b0a65404929641a9fabb6452b07fe4572b269d901d622d8a34a4e9043b2"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a96dc1fa45bd8c407a0af03b2d5218392729e1822b0c32e62c5bf7eeb5fb3958"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a7e65eb5c003a303b94aa2c3852ef130230ec79e349632d030e9571b87c4698c"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:999779addc413181912e984b942fbcc951be1f5b3663cd80b2687758f434c583"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:19a0f89a7bb9d8048d9c4370c9c543c396e894c76be5525f5e1ad287f1750ddf"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1ac2f5fe02fa75f56e1ad473f1175e11f475606ec9bd0be2e78e4734ad575034"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:574faa3b79e8ebac7cb1d7930f51184ba1ccf69adfdec53a12f319a06030a68b"}, - {file = "propcache-0.2.1-cp310-cp310-win32.whl", hash = "sha256:03ff9d3f665769b2a85e6157ac8b439644f2d7fd17615a82fa55739bc97863f4"}, - {file = "propcache-0.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:2d3af2e79991102678f53e0dbf4c35de99b6b8b58f29a27ca0325816364caaba"}, - {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ffc3cca89bb438fb9c95c13fc874012f7b9466b89328c3c8b1aa93cdcfadd16"}, - {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f174bbd484294ed9fdf09437f889f95807e5f229d5d93588d34e92106fbf6717"}, - {file = "propcache-0.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:70693319e0b8fd35dd863e3e29513875eb15c51945bf32519ef52927ca883bc3"}, - {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b480c6a4e1138e1aa137c0079b9b6305ec6dcc1098a8ca5196283e8a49df95a9"}, - {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d27b84d5880f6d8aa9ae3edb253c59d9f6642ffbb2c889b78b60361eed449787"}, - {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:857112b22acd417c40fa4595db2fe28ab900c8c5fe4670c7989b1c0230955465"}, - {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf6c4150f8c0e32d241436526f3c3f9cbd34429492abddbada2ffcff506c51af"}, - {file = "propcache-0.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66d4cfda1d8ed687daa4bc0274fcfd5267873db9a5bc0418c2da19273040eeb7"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2f992c07c0fca81655066705beae35fc95a2fa7366467366db627d9f2ee097f"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:4a571d97dbe66ef38e472703067021b1467025ec85707d57e78711c085984e54"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bb6178c241278d5fe853b3de743087be7f5f4c6f7d6d22a3b524d323eecec505"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ad1af54a62ffe39cf34db1aa6ed1a1873bd548f6401db39d8e7cd060b9211f82"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e7048abd75fe40712005bcfc06bb44b9dfcd8e101dda2ecf2f5aa46115ad07ca"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:160291c60081f23ee43d44b08a7e5fb76681221a8e10b3139618c5a9a291b84e"}, - {file = "propcache-0.2.1-cp311-cp311-win32.whl", hash = "sha256:819ce3b883b7576ca28da3861c7e1a88afd08cc8c96908e08a3f4dd64a228034"}, - {file = "propcache-0.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:edc9fc7051e3350643ad929df55c451899bb9ae6d24998a949d2e4c87fb596d3"}, - {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:081a430aa8d5e8876c6909b67bd2d937bfd531b0382d3fdedb82612c618bc41a"}, - {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2ccec9ac47cf4e04897619c0e0c1a48c54a71bdf045117d3a26f80d38ab1fb0"}, - {file = "propcache-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14d86fe14b7e04fa306e0c43cdbeebe6b2c2156a0c9ce56b815faacc193e320d"}, - {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:049324ee97bb67285b49632132db351b41e77833678432be52bdd0289c0e05e4"}, - {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cd9a1d071158de1cc1c71a26014dcdfa7dd3d5f4f88c298c7f90ad6f27bb46d"}, - {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98110aa363f1bb4c073e8dcfaefd3a5cea0f0834c2aab23dda657e4dab2f53b5"}, - {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:647894f5ae99c4cf6bb82a1bb3a796f6e06af3caa3d32e26d2350d0e3e3faf24"}, - {file = "propcache-0.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfd3223c15bebe26518d58ccf9a39b93948d3dcb3e57a20480dfdd315356baff"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d71264a80f3fcf512eb4f18f59423fe82d6e346ee97b90625f283df56aee103f"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e73091191e4280403bde6c9a52a6999d69cdfde498f1fdf629105247599b57ec"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3935bfa5fede35fb202c4b569bb9c042f337ca4ff7bd540a0aa5e37131659348"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f508b0491767bb1f2b87fdfacaba5f7eddc2f867740ec69ece6d1946d29029a6"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1672137af7c46662a1c2be1e8dc78cb6d224319aaa40271c9257d886be4363a6"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b74c261802d3d2b85c9df2dfb2fa81b6f90deeef63c2db9f0e029a3cac50b518"}, - {file = "propcache-0.2.1-cp312-cp312-win32.whl", hash = "sha256:d09c333d36c1409d56a9d29b3a1b800a42c76a57a5a8907eacdbce3f18768246"}, - {file = "propcache-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:c214999039d4f2a5b2073ac506bba279945233da8c786e490d411dfc30f855c1"}, - {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aca405706e0b0a44cc6bfd41fbe89919a6a56999157f6de7e182a990c36e37bc"}, - {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:12d1083f001ace206fe34b6bdc2cb94be66d57a850866f0b908972f90996b3e9"}, - {file = "propcache-0.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d93f3307ad32a27bda2e88ec81134b823c240aa3abb55821a8da553eed8d9439"}, - {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba278acf14471d36316159c94a802933d10b6a1e117b8554fe0d0d9b75c9d536"}, - {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4e6281aedfca15301c41f74d7005e6e3f4ca143584ba696ac69df4f02f40d629"}, - {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b750a8e5a1262434fb1517ddf64b5de58327f1adc3524a5e44c2ca43305eb0b"}, - {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf72af5e0fb40e9babf594308911436c8efde3cb5e75b6f206c34ad18be5c052"}, - {file = "propcache-0.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2d0a12018b04f4cb820781ec0dffb5f7c7c1d2a5cd22bff7fb055a2cb19ebce"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e800776a79a5aabdb17dcc2346a7d66d0777e942e4cd251defeb084762ecd17d"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:4160d9283bd382fa6c0c2b5e017acc95bc183570cd70968b9202ad6d8fc48dce"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:30b43e74f1359353341a7adb783c8f1b1c676367b011709f466f42fda2045e95"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:58791550b27d5488b1bb52bc96328456095d96206a250d28d874fafe11b3dfaf"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f022d381747f0dfe27e99d928e31bc51a18b65bb9e481ae0af1380a6725dd1f"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:297878dc9d0a334358f9b608b56d02e72899f3b8499fc6044133f0d319e2ec30"}, - {file = "propcache-0.2.1-cp313-cp313-win32.whl", hash = "sha256:ddfab44e4489bd79bda09d84c430677fc7f0a4939a73d2bba3073036f487a0a6"}, - {file = "propcache-0.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:556fc6c10989f19a179e4321e5d678db8eb2924131e64652a51fe83e4c3db0e1"}, - {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6a9a8c34fb7bb609419a211e59da8887eeca40d300b5ea8e56af98f6fbbb1541"}, - {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae1aa1cd222c6d205853b3013c69cd04515f9d6ab6de4b0603e2e1c33221303e"}, - {file = "propcache-0.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:accb6150ce61c9c4b7738d45550806aa2b71c7668c6942f17b0ac182b6142fd4"}, - {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5eee736daafa7af6d0a2dc15cc75e05c64f37fc37bafef2e00d77c14171c2097"}, - {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7a31fc1e1bd362874863fdeed71aed92d348f5336fd84f2197ba40c59f061bd"}, - {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba4cfa1052819d16699e1d55d18c92b6e094d4517c41dd231a8b9f87b6fa681"}, - {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f089118d584e859c62b3da0892b88a83d611c2033ac410e929cb6754eec0ed16"}, - {file = "propcache-0.2.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:781e65134efaf88feb447e8c97a51772aa75e48b794352f94cb7ea717dedda0d"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31f5af773530fd3c658b32b6bdc2d0838543de70eb9a2156c03e410f7b0d3aae"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:a7a078f5d37bee6690959c813977da5291b24286e7b962e62a94cec31aa5188b"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cea7daf9fc7ae6687cf1e2c049752f19f146fdc37c2cc376e7d0032cf4f25347"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:8b3489ff1ed1e8315674d0775dc7d2195fb13ca17b3808721b54dbe9fd020faf"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9403db39be1393618dd80c746cb22ccda168efce239c73af13c3763ef56ffc04"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5d97151bc92d2b2578ff7ce779cdb9174337390a535953cbb9452fb65164c587"}, - {file = "propcache-0.2.1-cp39-cp39-win32.whl", hash = "sha256:9caac6b54914bdf41bcc91e7eb9147d331d29235a7c967c150ef5df6464fd1bb"}, - {file = "propcache-0.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:92fc4500fcb33899b05ba73276dfb684a20d31caa567b7cb5252d48f896a91b1"}, - {file = "propcache-0.2.1-py3-none-any.whl", hash = "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54"}, - {file = "propcache-0.2.1.tar.gz", hash = "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64"}, + {file = "propcache-0.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f27785888d2fdd918bc36de8b8739f2d6c791399552333721b58193f68ea3e98"}, + {file = "propcache-0.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4e89cde74154c7b5957f87a355bb9c8ec929c167b59c83d90654ea36aeb6180"}, + {file = "propcache-0.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:730178f476ef03d3d4d255f0c9fa186cb1d13fd33ffe89d39f2cda4da90ceb71"}, + {file = "propcache-0.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:967a8eec513dbe08330f10137eacb427b2ca52118769e82ebcfcab0fba92a649"}, + {file = "propcache-0.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b9145c35cc87313b5fd480144f8078716007656093d23059e8993d3a8fa730f"}, + {file = "propcache-0.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e64e948ab41411958670f1093c0a57acfdc3bee5cf5b935671bbd5313bcf229"}, + {file = "propcache-0.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:319fa8765bfd6a265e5fa661547556da381e53274bc05094fc9ea50da51bfd46"}, + {file = "propcache-0.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c66d8ccbc902ad548312b96ed8d5d266d0d2c6d006fd0f66323e9d8f2dd49be7"}, + {file = "propcache-0.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2d219b0dbabe75e15e581fc1ae796109b07c8ba7d25b9ae8d650da582bed01b0"}, + {file = "propcache-0.3.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:cd6a55f65241c551eb53f8cf4d2f4af33512c39da5d9777694e9d9c60872f519"}, + {file = "propcache-0.3.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9979643ffc69b799d50d3a7b72b5164a2e97e117009d7af6dfdd2ab906cb72cd"}, + {file = "propcache-0.3.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4cf9e93a81979f1424f1a3d155213dc928f1069d697e4353edb8a5eba67c6259"}, + {file = "propcache-0.3.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2fce1df66915909ff6c824bbb5eb403d2d15f98f1518e583074671a30fe0c21e"}, + {file = "propcache-0.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4d0dfdd9a2ebc77b869a0b04423591ea8823f791293b527dc1bb896c1d6f1136"}, + {file = "propcache-0.3.1-cp310-cp310-win32.whl", hash = "sha256:1f6cc0ad7b4560e5637eb2c994e97b4fa41ba8226069c9277eb5ea7101845b42"}, + {file = "propcache-0.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:47ef24aa6511e388e9894ec16f0fbf3313a53ee68402bc428744a367ec55b833"}, + {file = "propcache-0.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7f30241577d2fef2602113b70ef7231bf4c69a97e04693bde08ddab913ba0ce5"}, + {file = "propcache-0.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:43593c6772aa12abc3af7784bff4a41ffa921608dd38b77cf1dfd7f5c4e71371"}, + {file = "propcache-0.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a75801768bbe65499495660b777e018cbe90c7980f07f8aa57d6be79ea6f71da"}, + {file = "propcache-0.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6f1324db48f001c2ca26a25fa25af60711e09b9aaf4b28488602776f4f9a744"}, + {file = "propcache-0.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cdb0f3e1eb6dfc9965d19734d8f9c481b294b5274337a8cb5cb01b462dcb7e0"}, + {file = "propcache-0.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1eb34d90aac9bfbced9a58b266f8946cb5935869ff01b164573a7634d39fbcb5"}, + {file = "propcache-0.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f35c7070eeec2cdaac6fd3fe245226ed2a6292d3ee8c938e5bb645b434c5f256"}, + {file = "propcache-0.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b23c11c2c9e6d4e7300c92e022046ad09b91fd00e36e83c44483df4afa990073"}, + {file = "propcache-0.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3e19ea4ea0bf46179f8a3652ac1426e6dcbaf577ce4b4f65be581e237340420d"}, + {file = "propcache-0.3.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bd39c92e4c8f6cbf5f08257d6360123af72af9f4da75a690bef50da77362d25f"}, + {file = "propcache-0.3.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b0313e8b923b3814d1c4a524c93dfecea5f39fa95601f6a9b1ac96cd66f89ea0"}, + {file = "propcache-0.3.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e861ad82892408487be144906a368ddbe2dc6297074ade2d892341b35c59844a"}, + {file = "propcache-0.3.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:61014615c1274df8da5991a1e5da85a3ccb00c2d4701ac6f3383afd3ca47ab0a"}, + {file = "propcache-0.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:71ebe3fe42656a2328ab08933d420df5f3ab121772eef78f2dc63624157f0ed9"}, + {file = "propcache-0.3.1-cp311-cp311-win32.whl", hash = "sha256:58aa11f4ca8b60113d4b8e32d37e7e78bd8af4d1a5b5cb4979ed856a45e62005"}, + {file = "propcache-0.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:9532ea0b26a401264b1365146c440a6d78269ed41f83f23818d4b79497aeabe7"}, + {file = "propcache-0.3.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f78eb8422acc93d7b69964012ad7048764bb45a54ba7a39bb9e146c72ea29723"}, + {file = "propcache-0.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:89498dd49c2f9a026ee057965cdf8192e5ae070ce7d7a7bd4b66a8e257d0c976"}, + {file = "propcache-0.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:09400e98545c998d57d10035ff623266927cb784d13dd2b31fd33b8a5316b85b"}, + {file = "propcache-0.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa8efd8c5adc5a2c9d3b952815ff8f7710cefdcaf5f2c36d26aff51aeca2f12f"}, + {file = "propcache-0.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2fe5c910f6007e716a06d269608d307b4f36e7babee5f36533722660e8c4a70"}, + {file = "propcache-0.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a0ab8cf8cdd2194f8ff979a43ab43049b1df0b37aa64ab7eca04ac14429baeb7"}, + {file = "propcache-0.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:563f9d8c03ad645597b8d010ef4e9eab359faeb11a0a2ac9f7b4bc8c28ebef25"}, + {file = "propcache-0.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb6e0faf8cb6b4beea5d6ed7b5a578254c6d7df54c36ccd3d8b3eb00d6770277"}, + {file = "propcache-0.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1c5c7ab7f2bb3f573d1cb921993006ba2d39e8621019dffb1c5bc94cdbae81e8"}, + {file = "propcache-0.3.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:050b571b2e96ec942898f8eb46ea4bfbb19bd5502424747e83badc2d4a99a44e"}, + {file = "propcache-0.3.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e1c4d24b804b3a87e9350f79e2371a705a188d292fd310e663483af6ee6718ee"}, + {file = "propcache-0.3.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e4fe2a6d5ce975c117a6bb1e8ccda772d1e7029c1cca1acd209f91d30fa72815"}, + {file = "propcache-0.3.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:feccd282de1f6322f56f6845bf1207a537227812f0a9bf5571df52bb418d79d5"}, + {file = "propcache-0.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ec314cde7314d2dd0510c6787326bbffcbdc317ecee6b7401ce218b3099075a7"}, + {file = "propcache-0.3.1-cp312-cp312-win32.whl", hash = "sha256:7d2d5a0028d920738372630870e7d9644ce437142197f8c827194fca404bf03b"}, + {file = "propcache-0.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:88c423efef9d7a59dae0614eaed718449c09a5ac79a5f224a8b9664d603f04a3"}, + {file = "propcache-0.3.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f1528ec4374617a7a753f90f20e2f551121bb558fcb35926f99e3c42367164b8"}, + {file = "propcache-0.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dc1915ec523b3b494933b5424980831b636fe483d7d543f7afb7b3bf00f0c10f"}, + {file = "propcache-0.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a110205022d077da24e60b3df8bcee73971be9575dec5573dd17ae5d81751111"}, + {file = "propcache-0.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d249609e547c04d190e820d0d4c8ca03ed4582bcf8e4e160a6969ddfb57b62e5"}, + {file = "propcache-0.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ced33d827625d0a589e831126ccb4f5c29dfdf6766cac441d23995a65825dcb"}, + {file = "propcache-0.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4114c4ada8f3181af20808bedb250da6bae56660e4b8dfd9cd95d4549c0962f7"}, + {file = "propcache-0.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:975af16f406ce48f1333ec5e912fe11064605d5c5b3f6746969077cc3adeb120"}, + {file = "propcache-0.3.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a34aa3a1abc50740be6ac0ab9d594e274f59960d3ad253cd318af76b996dd654"}, + {file = "propcache-0.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9cec3239c85ed15bfaded997773fdad9fb5662b0a7cbc854a43f291eb183179e"}, + {file = "propcache-0.3.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:05543250deac8e61084234d5fc54f8ebd254e8f2b39a16b1dce48904f45b744b"}, + {file = "propcache-0.3.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5cb5918253912e088edbf023788de539219718d3b10aef334476b62d2b53de53"}, + {file = "propcache-0.3.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f3bbecd2f34d0e6d3c543fdb3b15d6b60dd69970c2b4c822379e5ec8f6f621d5"}, + {file = "propcache-0.3.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aca63103895c7d960a5b9b044a83f544b233c95e0dcff114389d64d762017af7"}, + {file = "propcache-0.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a0a9898fdb99bf11786265468571e628ba60af80dc3f6eb89a3545540c6b0ef"}, + {file = "propcache-0.3.1-cp313-cp313-win32.whl", hash = "sha256:3a02a28095b5e63128bcae98eb59025924f121f048a62393db682f049bf4ac24"}, + {file = "propcache-0.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:813fbb8b6aea2fc9659815e585e548fe706d6f663fa73dff59a1677d4595a037"}, + {file = "propcache-0.3.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a444192f20f5ce8a5e52761a031b90f5ea6288b1eef42ad4c7e64fef33540b8f"}, + {file = "propcache-0.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0fbe94666e62ebe36cd652f5fc012abfbc2342de99b523f8267a678e4dfdee3c"}, + {file = "propcache-0.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f011f104db880f4e2166bcdcf7f58250f7a465bc6b068dc84c824a3d4a5c94dc"}, + {file = "propcache-0.3.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e584b6d388aeb0001d6d5c2bd86b26304adde6d9bb9bfa9c4889805021b96de"}, + {file = "propcache-0.3.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a17583515a04358b034e241f952f1715243482fc2c2945fd99a1b03a0bd77d6"}, + {file = "propcache-0.3.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5aed8d8308215089c0734a2af4f2e95eeb360660184ad3912686c181e500b2e7"}, + {file = "propcache-0.3.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d8e309ff9a0503ef70dc9a0ebd3e69cf7b3894c9ae2ae81fc10943c37762458"}, + {file = "propcache-0.3.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b655032b202028a582d27aeedc2e813299f82cb232f969f87a4fde491a233f11"}, + {file = "propcache-0.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f64d91b751df77931336b5ff7bafbe8845c5770b06630e27acd5dbb71e1931c"}, + {file = "propcache-0.3.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:19a06db789a4bd896ee91ebc50d059e23b3639c25d58eb35be3ca1cbe967c3bf"}, + {file = "propcache-0.3.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:bef100c88d8692864651b5f98e871fb090bd65c8a41a1cb0ff2322db39c96c27"}, + {file = "propcache-0.3.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:87380fb1f3089d2a0b8b00f006ed12bd41bd858fabfa7330c954c70f50ed8757"}, + {file = "propcache-0.3.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e474fc718e73ba5ec5180358aa07f6aded0ff5f2abe700e3115c37d75c947e18"}, + {file = "propcache-0.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:17d1c688a443355234f3c031349da69444be052613483f3e4158eef751abcd8a"}, + {file = "propcache-0.3.1-cp313-cp313t-win32.whl", hash = "sha256:359e81a949a7619802eb601d66d37072b79b79c2505e6d3fd8b945538411400d"}, + {file = "propcache-0.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e7fb9a84c9abbf2b2683fa3e7b0d7da4d8ecf139a1c635732a8bda29c5214b0e"}, + {file = "propcache-0.3.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ed5f6d2edbf349bd8d630e81f474d33d6ae5d07760c44d33cd808e2f5c8f4ae6"}, + {file = "propcache-0.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:668ddddc9f3075af019f784456267eb504cb77c2c4bd46cc8402d723b4d200bf"}, + {file = "propcache-0.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0c86e7ceea56376216eba345aa1fc6a8a6b27ac236181f840d1d7e6a1ea9ba5c"}, + {file = "propcache-0.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83be47aa4e35b87c106fc0c84c0fc069d3f9b9b06d3c494cd404ec6747544894"}, + {file = "propcache-0.3.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:27c6ac6aa9fc7bc662f594ef380707494cb42c22786a558d95fcdedb9aa5d035"}, + {file = "propcache-0.3.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64a956dff37080b352c1c40b2966b09defb014347043e740d420ca1eb7c9b908"}, + {file = "propcache-0.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82de5da8c8893056603ac2d6a89eb8b4df49abf1a7c19d536984c8dd63f481d5"}, + {file = "propcache-0.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c3c3a203c375b08fd06a20da3cf7aac293b834b6f4f4db71190e8422750cca5"}, + {file = "propcache-0.3.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b303b194c2e6f171cfddf8b8ba30baefccf03d36a4d9cab7fd0bb68ba476a3d7"}, + {file = "propcache-0.3.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:916cd229b0150129d645ec51614d38129ee74c03293a9f3f17537be0029a9641"}, + {file = "propcache-0.3.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:a461959ead5b38e2581998700b26346b78cd98540b5524796c175722f18b0294"}, + {file = "propcache-0.3.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:069e7212890b0bcf9b2be0a03afb0c2d5161d91e1bf51569a64f629acc7defbf"}, + {file = "propcache-0.3.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ef2e4e91fb3945769e14ce82ed53007195e616a63aa43b40fb7ebaaf907c8d4c"}, + {file = "propcache-0.3.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8638f99dca15b9dff328fb6273e09f03d1c50d9b6512f3b65a4154588a7595fe"}, + {file = "propcache-0.3.1-cp39-cp39-win32.whl", hash = "sha256:6f173bbfe976105aaa890b712d1759de339d8a7cef2fc0a1714cc1a1e1c47f64"}, + {file = "propcache-0.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:603f1fe4144420374f1a69b907494c3acbc867a581c2d49d4175b0de7cc64566"}, + {file = "propcache-0.3.1-py3-none-any.whl", hash = "sha256:9a8ecf38de50a7f518c21568c80f985e776397b902f1ce0b01f799aba1608b40"}, + {file = "propcache-0.3.1.tar.gz", hash = "sha256:40d980c33765359098837527e18eddefc9a24cea5b45e078a7f3bb5b032c6ecf"}, ] [[package]] name = "protobuf" -version = "5.29.2" +version = "6.31.0" description = "" optional = false -python-versions = ">=3.8" -groups = ["main"] +python-versions = ">=3.9" files = [ - {file = "protobuf-5.29.2-cp310-abi3-win32.whl", hash = "sha256:c12ba8249f5624300cf51c3d0bfe5be71a60c63e4dcf51ffe9a68771d958c851"}, - {file = "protobuf-5.29.2-cp310-abi3-win_amd64.whl", hash = "sha256:842de6d9241134a973aab719ab42b008a18a90f9f07f06ba480df268f86432f9"}, - {file = "protobuf-5.29.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a0c53d78383c851bfa97eb42e3703aefdc96d2036a41482ffd55dc5f529466eb"}, - {file = "protobuf-5.29.2-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:494229ecd8c9009dd71eda5fd57528395d1eacdf307dbece6c12ad0dd09e912e"}, - {file = "protobuf-5.29.2-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:b6b0d416bbbb9d4fbf9d0561dbfc4e324fd522f61f7af0fe0f282ab67b22477e"}, - {file = "protobuf-5.29.2-cp38-cp38-win32.whl", hash = "sha256:e621a98c0201a7c8afe89d9646859859be97cb22b8bf1d8eacfd90d5bda2eb19"}, - {file = "protobuf-5.29.2-cp38-cp38-win_amd64.whl", hash = "sha256:13d6d617a2a9e0e82a88113d7191a1baa1e42c2cc6f5f1398d3b054c8e7e714a"}, - {file = "protobuf-5.29.2-cp39-cp39-win32.whl", hash = "sha256:36000f97ea1e76e8398a3f02936aac2a5d2b111aae9920ec1b769fc4a222c4d9"}, - {file = "protobuf-5.29.2-cp39-cp39-win_amd64.whl", hash = "sha256:2d2e674c58a06311c8e99e74be43e7f3a8d1e2b2fdf845eaa347fbd866f23355"}, - {file = "protobuf-5.29.2-py3-none-any.whl", hash = "sha256:fde4554c0e578a5a0bcc9a276339594848d1e89f9ea47b4427c80e5d72f90181"}, - {file = "protobuf-5.29.2.tar.gz", hash = "sha256:b2cc8e8bb7c9326996f0e160137b0861f1a82162502658df2951209d0cb0309e"}, + {file = "protobuf-6.31.0-cp310-abi3-win32.whl", hash = "sha256:10bd62802dfa0588649740a59354090eaf54b8322f772fbdcca19bc78d27f0d6"}, + {file = "protobuf-6.31.0-cp310-abi3-win_amd64.whl", hash = "sha256:3e987c99fd634be8347246a02123250f394ba20573c953de133dc8b2c107dd71"}, + {file = "protobuf-6.31.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:2c812f0f96ceb6b514448cefeb1df54ec06dde456783f5099c0e2f8a0f2caa89"}, + {file = "protobuf-6.31.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:67ce50195e4e584275623b8e6bc6d3d3dfd93924bf6116b86b3b8975ab9e4571"}, + {file = "protobuf-6.31.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:5353e38844168a327acd2b2aa440044411cd8d1b6774d5701008bd1dba067c79"}, + {file = "protobuf-6.31.0-cp39-cp39-win32.whl", hash = "sha256:96d8da25c83b11db5fe9e0376351ce25e7205e13224d939e097b6f82a72af824"}, + {file = "protobuf-6.31.0-cp39-cp39-win_amd64.whl", hash = "sha256:00a873c06efdfb854145d9ded730b09cf57d206075c38132674093370e2edabb"}, + {file = "protobuf-6.31.0-py3-none-any.whl", hash = "sha256:6ac2e82556e822c17a8d23aa1190bbc1d06efb9c261981da95c71c9da09e9e23"}, + {file = "protobuf-6.31.0.tar.gz", hash = "sha256:314fab1a6a316469dc2dd46f993cbbe95c861ea6807da910becfe7475bc26ffe"}, ] [[package]] @@ -1249,7 +1236,6 @@ version = "2.13.0" description = "Python style guide checker" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "pycodestyle-2.13.0-py2.py3-none-any.whl", hash = "sha256:35863c5974a271c7a726ed228a14a4f6daf49df369d8c50cd9a6f58a5e143ba9"}, {file = "pycodestyle-2.13.0.tar.gz", hash = "sha256:c8415bf09abe81d9c7f872502a6eee881fbe85d8763dd5b9924bb0a01d67efae"}, @@ -1261,8 +1247,6 @@ version = "2.22" description = "C parser in Python" optional = false python-versions = ">=3.8" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\"" files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, @@ -1274,7 +1258,6 @@ version = "3.3.2" description = "passive checker of Python programs" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "pyflakes-3.3.2-py2.py3-none-any.whl", hash = "sha256:5039c8339cbb1944045f4ee5466908906180f13cc99cc9949348d10f82a5c32a"}, {file = "pyflakes-3.3.2.tar.gz", hash = "sha256:6dfd61d87b97fba5dcfaaf781171ac16be16453be6d816147989e7f6e6a9576b"}, @@ -1282,14 +1265,13 @@ files = [ [[package]] name = "pygments" -version = "2.19.0" +version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ - {file = "pygments-2.19.0-py3-none-any.whl", hash = "sha256:4755e6e64d22161d5b61432c0600c923c5927214e7c956e31c23923c89251a9b"}, - {file = "pygments-2.19.0.tar.gz", hash = "sha256:afc4146269910d4bdfabcd27c24923137a74d562a23a320a41a55ad303e19783"}, + {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, + {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, ] [package.extras] @@ -1301,7 +1283,6 @@ version = "8.3.5" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, @@ -1324,8 +1305,6 @@ version = "0.2.3" description = "A (partial) reimplementation of pywin32 using ctypes/cffi" optional = false python-versions = ">=3.6" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"win32\"" files = [ {file = "pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755"}, {file = "pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8"}, @@ -1337,7 +1316,6 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -1400,7 +1378,6 @@ version = "44.0" description = "readme_renderer is a library for rendering readme descriptions for Warehouse" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "readme_renderer-44.0-py3-none-any.whl", hash = "sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151"}, {file = "readme_renderer-44.0.tar.gz", hash = "sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1"}, @@ -1420,7 +1397,6 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -1442,7 +1418,6 @@ version = "1.0.0" description = "A utility belt for advanced users of python-requests" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["dev"] files = [ {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, @@ -1457,7 +1432,6 @@ version = "2.0.0" description = "Validating URI References per RFC 3986" optional = false python-versions = ">=3.7" -groups = ["dev"] files = [ {file = "rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd"}, {file = "rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c"}, @@ -1468,14 +1442,13 @@ idna2008 = ["idna"] [[package]] name = "rich" -version = "13.9.4" +version = "14.0.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" -groups = ["dev"] files = [ - {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, - {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, + {file = "rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0"}, + {file = "rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725"}, ] [package.dependencies] @@ -1492,8 +1465,6 @@ version = "3.3.3" description = "Python bindings to FreeDesktop.org Secret Service API" optional = false python-versions = ">=3.6" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" files = [ {file = "SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"}, {file = "SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"}, @@ -1509,8 +1480,6 @@ version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" -groups = ["dev"] -markers = "python_version < \"3.11\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, @@ -1552,7 +1521,6 @@ version = "6.1.0" description = "Collection of utilities for publishing packages on PyPI" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "twine-6.1.0-py3-none-any.whl", hash = "sha256:a47f973caf122930bf0fbbf17f80b83bc1602c9ce393c7845f289a3001dc5384"}, {file = "twine-6.1.0.tar.gz", hash = "sha256:be324f6272eff91d07ee93f251edf232fc647935dd585ac003539b42404a8dbd"}, @@ -1575,31 +1543,28 @@ keyring = ["keyring (>=15.1)"] [[package]] name = "typing-extensions" -version = "4.12.2" +version = "4.13.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] -markers = "python_version < \"3.11\"" files = [ - {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, - {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, + {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, + {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, ] [[package]] name = "urllib3" -version = "2.3.0" +version = "2.4.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" -groups = ["main", "dev"] files = [ - {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, - {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, + {file = "urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813"}, + {file = "urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466"}, ] [package.extras] -brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -1610,7 +1575,6 @@ version = "1.8.0" description = "WebSocket client for Python with low level API options" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526"}, {file = "websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da"}, @@ -1623,100 +1587,121 @@ test = ["websockets"] [[package]] name = "yarl" -version = "1.18.3" +version = "1.20.0" description = "Yet another URL library" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ - {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"}, - {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"}, - {file = "yarl-1.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:602d98f2c2d929f8e697ed274fbadc09902c4025c5a9963bf4e9edfc3ab6f7ed"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c654d5207c78e0bd6d749f6dae1dcbbfde3403ad3a4b11f3c5544d9906969dde"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5094d9206c64181d0f6e76ebd8fb2f8fe274950a63890ee9e0ebfd58bf9d787b"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35098b24e0327fc4ebdc8ffe336cee0a87a700c24ffed13161af80124b7dc8e5"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3236da9272872443f81fedc389bace88408f64f89f75d1bdb2256069a8730ccc"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2c08cc9b16f4f4bc522771d96734c7901e7ebef70c6c5c35dd0f10845270bcd"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80316a8bd5109320d38eef8833ccf5f89608c9107d02d2a7f985f98ed6876990"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1e1cc06da1491e6734f0ea1e6294ce00792193c463350626571c287c9a704db"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fea09ca13323376a2fdfb353a5fa2e59f90cd18d7ca4eaa1fd31f0a8b4f91e62"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e3b9fd71836999aad54084906f8663dffcd2a7fb5cdafd6c37713b2e72be1760"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:757e81cae69244257d125ff31663249b3013b5dc0a8520d73694aed497fb195b"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b1771de9944d875f1b98a745bc547e684b863abf8f8287da8466cf470ef52690"}, - {file = "yarl-1.18.3-cp310-cp310-win32.whl", hash = "sha256:8874027a53e3aea659a6d62751800cf6e63314c160fd607489ba5c2edd753cf6"}, - {file = "yarl-1.18.3-cp310-cp310-win_amd64.whl", hash = "sha256:93b2e109287f93db79210f86deb6b9bbb81ac32fc97236b16f7433db7fc437d8"}, - {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8503ad47387b8ebd39cbbbdf0bf113e17330ffd339ba1144074da24c545f0069"}, - {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02ddb6756f8f4517a2d5e99d8b2f272488e18dd0bfbc802f31c16c6c20f22193"}, - {file = "yarl-1.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a283dd2882ac98cc6318384f565bffc751ab564605959df4752d42483ad889"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d980e0325b6eddc81331d3f4551e2a333999fb176fd153e075c6d1c2530aa8a8"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b643562c12680b01e17239be267bc306bbc6aac1f34f6444d1bded0c5ce438ca"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c017a3b6df3a1bd45b9fa49a0f54005e53fbcad16633870104b66fa1a30a29d8"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75674776d96d7b851b6498f17824ba17849d790a44d282929c42dbb77d4f17ae"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccaa3a4b521b780a7e771cc336a2dba389a0861592bbce09a476190bb0c8b4b3"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d06d3005e668744e11ed80812e61efd77d70bb7f03e33c1598c301eea20efbb"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9d41beda9dc97ca9ab0b9888cb71f7539124bc05df02c0cff6e5acc5a19dcc6e"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ba23302c0c61a9999784e73809427c9dbedd79f66a13d84ad1b1943802eaaf59"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6748dbf9bfa5ba1afcc7556b71cda0d7ce5f24768043a02a58846e4a443d808d"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0b0cad37311123211dc91eadcb322ef4d4a66008d3e1bdc404808992260e1a0e"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fb2171a4486bb075316ee754c6d8382ea6eb8b399d4ec62fde2b591f879778a"}, - {file = "yarl-1.18.3-cp311-cp311-win32.whl", hash = "sha256:61b1a825a13bef4a5f10b1885245377d3cd0bf87cba068e1d9a88c2ae36880e1"}, - {file = "yarl-1.18.3-cp311-cp311-win_amd64.whl", hash = "sha256:b9d60031cf568c627d028239693fd718025719c02c9f55df0a53e587aab951b5"}, - {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50"}, - {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576"}, - {file = "yarl-1.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285"}, - {file = "yarl-1.18.3-cp312-cp312-win32.whl", hash = "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2"}, - {file = "yarl-1.18.3-cp312-cp312-win_amd64.whl", hash = "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477"}, - {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:90adb47ad432332d4f0bc28f83a5963f426ce9a1a8809f5e584e704b82685dcb"}, - {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:913829534200eb0f789d45349e55203a091f45c37a2674678744ae52fae23efa"}, - {file = "yarl-1.18.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef9f7768395923c3039055c14334ba4d926f3baf7b776c923c93d80195624782"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a19f62ff30117e706ebc9090b8ecc79aeb77d0b1f5ec10d2d27a12bc9f66d0"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e17c9361d46a4d5addf777c6dd5eab0715a7684c2f11b88c67ac37edfba6c482"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a74a13a4c857a84a845505fd2d68e54826a2cd01935a96efb1e9d86c728e186"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f7ce59d6ee7741af71d82020346af364949314ed3d87553763a2df1829cc58"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f52a265001d830bc425f82ca9eabda94a64a4d753b07d623a9f2863fde532b53"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:82123d0c954dc58db301f5021a01854a85bf1f3bb7d12ae0c01afc414a882ca2"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2ec9bbba33b2d00999af4631a3397d1fd78290c48e2a3e52d8dd72db3a067ac8"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fbd6748e8ab9b41171bb95c6142faf068f5ef1511935a0aa07025438dd9a9bc1"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:877d209b6aebeb5b16c42cbb377f5f94d9e556626b1bfff66d7b0d115be88d0a"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b464c4ab4bfcb41e3bfd3f1c26600d038376c2de3297760dfe064d2cb7ea8e10"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8d39d351e7faf01483cc7ff7c0213c412e38e5a340238826be7e0e4da450fdc8"}, - {file = "yarl-1.18.3-cp313-cp313-win32.whl", hash = "sha256:61ee62ead9b68b9123ec24bc866cbef297dd266175d53296e2db5e7f797f902d"}, - {file = "yarl-1.18.3-cp313-cp313-win_amd64.whl", hash = "sha256:578e281c393af575879990861823ef19d66e2b1d0098414855dd367e234f5b3c"}, - {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:61e5e68cb65ac8f547f6b5ef933f510134a6bf31bb178be428994b0cb46c2a04"}, - {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe57328fbc1bfd0bd0514470ac692630f3901c0ee39052ae47acd1d90a436719"}, - {file = "yarl-1.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a440a2a624683108a1b454705ecd7afc1c3438a08e890a1513d468671d90a04e"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c7907c8548bcd6ab860e5f513e727c53b4a714f459b084f6580b49fa1b9cee"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4f6450109834af88cb4cc5ecddfc5380ebb9c228695afc11915a0bf82116789"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9ca04806f3be0ac6d558fffc2fdf8fcef767e0489d2684a21912cc4ed0cd1b8"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77a6e85b90a7641d2e07184df5557132a337f136250caafc9ccaa4a2a998ca2c"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6333c5a377c8e2f5fae35e7b8f145c617b02c939d04110c76f29ee3676b5f9a5"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0b3c92fa08759dbf12b3a59579a4096ba9af8dd344d9a813fc7f5070d86bbab1"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4ac515b860c36becb81bb84b667466885096b5fc85596948548b667da3bf9f24"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:045b8482ce9483ada4f3f23b3774f4e1bf4f23a2d5c912ed5170f68efb053318"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:a4bb030cf46a434ec0225bddbebd4b89e6471814ca851abb8696170adb163985"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:54d6921f07555713b9300bee9c50fb46e57e2e639027089b1d795ecd9f7fa910"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1d407181cfa6e70077df3377938c08012d18893f9f20e92f7d2f314a437c30b1"}, - {file = "yarl-1.18.3-cp39-cp39-win32.whl", hash = "sha256:ac36703a585e0929b032fbaab0707b75dc12703766d0b53486eabd5139ebadd5"}, - {file = "yarl-1.18.3-cp39-cp39-win_amd64.whl", hash = "sha256:ba87babd629f8af77f557b61e49e7c7cac36f22f871156b91e10a6e9d4f829e9"}, - {file = "yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b"}, - {file = "yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1"}, + {file = "yarl-1.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f1f6670b9ae3daedb325fa55fbe31c22c8228f6e0b513772c2e1c623caa6ab22"}, + {file = "yarl-1.20.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85a231fa250dfa3308f3c7896cc007a47bc76e9e8e8595c20b7426cac4884c62"}, + {file = "yarl-1.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a06701b647c9939d7019acdfa7ebbfbb78ba6aa05985bb195ad716ea759a569"}, + {file = "yarl-1.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7595498d085becc8fb9203aa314b136ab0516c7abd97e7d74f7bb4eb95042abe"}, + {file = "yarl-1.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af5607159085dcdb055d5678fc2d34949bd75ae6ea6b4381e784bbab1c3aa195"}, + {file = "yarl-1.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:95b50910e496567434cb77a577493c26bce0f31c8a305135f3bda6a2483b8e10"}, + {file = "yarl-1.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b594113a301ad537766b4e16a5a6750fcbb1497dcc1bc8a4daae889e6402a634"}, + {file = "yarl-1.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:083ce0393ea173cd37834eb84df15b6853b555d20c52703e21fbababa8c129d2"}, + {file = "yarl-1.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f1a350a652bbbe12f666109fbddfdf049b3ff43696d18c9ab1531fbba1c977a"}, + {file = "yarl-1.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fb0caeac4a164aadce342f1597297ec0ce261ec4532bbc5a9ca8da5622f53867"}, + {file = "yarl-1.20.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:d88cc43e923f324203f6ec14434fa33b85c06d18d59c167a0637164863b8e995"}, + {file = "yarl-1.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e52d6ed9ea8fd3abf4031325dc714aed5afcbfa19ee4a89898d663c9976eb487"}, + {file = "yarl-1.20.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ce360ae48a5e9961d0c730cf891d40698a82804e85f6e74658fb175207a77cb2"}, + {file = "yarl-1.20.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:06d06c9d5b5bc3eb56542ceeba6658d31f54cf401e8468512447834856fb0e61"}, + {file = "yarl-1.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c27d98f4e5c4060582f44e58309c1e55134880558f1add7a87c1bc36ecfade19"}, + {file = "yarl-1.20.0-cp310-cp310-win32.whl", hash = "sha256:f4d3fa9b9f013f7050326e165c3279e22850d02ae544ace285674cb6174b5d6d"}, + {file = "yarl-1.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:bc906b636239631d42eb8a07df8359905da02704a868983265603887ed68c076"}, + {file = "yarl-1.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fdb5204d17cb32b2de2d1e21c7461cabfacf17f3645e4b9039f210c5d3378bf3"}, + {file = "yarl-1.20.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:eaddd7804d8e77d67c28d154ae5fab203163bd0998769569861258e525039d2a"}, + {file = "yarl-1.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:634b7ba6b4a85cf67e9df7c13a7fb2e44fa37b5d34501038d174a63eaac25ee2"}, + {file = "yarl-1.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d409e321e4addf7d97ee84162538c7258e53792eb7c6defd0c33647d754172e"}, + {file = "yarl-1.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ea52f7328a36960ba3231c6677380fa67811b414798a6e071c7085c57b6d20a9"}, + {file = "yarl-1.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c8703517b924463994c344dcdf99a2d5ce9eca2b6882bb640aa555fb5efc706a"}, + {file = "yarl-1.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:077989b09ffd2f48fb2d8f6a86c5fef02f63ffe6b1dd4824c76de7bb01e4f2e2"}, + {file = "yarl-1.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0acfaf1da020253f3533526e8b7dd212838fdc4109959a2c53cafc6db611bff2"}, + {file = "yarl-1.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4230ac0b97ec5eeb91d96b324d66060a43fd0d2a9b603e3327ed65f084e41f8"}, + {file = "yarl-1.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a6a1e6ae21cdd84011c24c78d7a126425148b24d437b5702328e4ba640a8902"}, + {file = "yarl-1.20.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:86de313371ec04dd2531f30bc41a5a1a96f25a02823558ee0f2af0beaa7ca791"}, + {file = "yarl-1.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:dd59c9dd58ae16eaa0f48c3d0cbe6be8ab4dc7247c3ff7db678edecbaf59327f"}, + {file = "yarl-1.20.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a0bc5e05f457b7c1994cc29e83b58f540b76234ba6b9648a4971ddc7f6aa52da"}, + {file = "yarl-1.20.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c9471ca18e6aeb0e03276b5e9b27b14a54c052d370a9c0c04a68cefbd1455eb4"}, + {file = "yarl-1.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:40ed574b4df723583a26c04b298b283ff171bcc387bc34c2683235e2487a65a5"}, + {file = "yarl-1.20.0-cp311-cp311-win32.whl", hash = "sha256:db243357c6c2bf3cd7e17080034ade668d54ce304d820c2a58514a4e51d0cfd6"}, + {file = "yarl-1.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:8c12cd754d9dbd14204c328915e23b0c361b88f3cffd124129955e60a4fbfcfb"}, + {file = "yarl-1.20.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e06b9f6cdd772f9b665e5ba8161968e11e403774114420737f7884b5bd7bdf6f"}, + {file = "yarl-1.20.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b9ae2fbe54d859b3ade40290f60fe40e7f969d83d482e84d2c31b9bff03e359e"}, + {file = "yarl-1.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6d12b8945250d80c67688602c891237994d203d42427cb14e36d1a732eda480e"}, + {file = "yarl-1.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:087e9731884621b162a3e06dc0d2d626e1542a617f65ba7cc7aeab279d55ad33"}, + {file = "yarl-1.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:69df35468b66c1a6e6556248e6443ef0ec5f11a7a4428cf1f6281f1879220f58"}, + {file = "yarl-1.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b2992fe29002fd0d4cbaea9428b09af9b8686a9024c840b8a2b8f4ea4abc16f"}, + {file = "yarl-1.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4c903e0b42aab48abfbac668b5a9d7b6938e721a6341751331bcd7553de2dcae"}, + {file = "yarl-1.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf099e2432131093cc611623e0b0bcc399b8cddd9a91eded8bfb50402ec35018"}, + {file = "yarl-1.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a7f62f5dc70a6c763bec9ebf922be52aa22863d9496a9a30124d65b489ea672"}, + {file = "yarl-1.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:54ac15a8b60382b2bcefd9a289ee26dc0920cf59b05368c9b2b72450751c6eb8"}, + {file = "yarl-1.20.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:25b3bc0763a7aca16a0f1b5e8ef0f23829df11fb539a1b70476dcab28bd83da7"}, + {file = "yarl-1.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b2586e36dc070fc8fad6270f93242124df68b379c3a251af534030a4a33ef594"}, + {file = "yarl-1.20.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:866349da9d8c5290cfefb7fcc47721e94de3f315433613e01b435473be63daa6"}, + {file = "yarl-1.20.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:33bb660b390a0554d41f8ebec5cd4475502d84104b27e9b42f5321c5192bfcd1"}, + {file = "yarl-1.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:737e9f171e5a07031cbee5e9180f6ce21a6c599b9d4b2c24d35df20a52fabf4b"}, + {file = "yarl-1.20.0-cp312-cp312-win32.whl", hash = "sha256:839de4c574169b6598d47ad61534e6981979ca2c820ccb77bf70f4311dd2cc64"}, + {file = "yarl-1.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:3d7dbbe44b443b0c4aa0971cb07dcb2c2060e4a9bf8d1301140a33a93c98e18c"}, + {file = "yarl-1.20.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2137810a20b933b1b1b7e5cf06a64c3ed3b4747b0e5d79c9447c00db0e2f752f"}, + {file = "yarl-1.20.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:447c5eadd750db8389804030d15f43d30435ed47af1313303ed82a62388176d3"}, + {file = "yarl-1.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42fbe577272c203528d402eec8bf4b2d14fd49ecfec92272334270b850e9cd7d"}, + {file = "yarl-1.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18e321617de4ab170226cd15006a565d0fa0d908f11f724a2c9142d6b2812ab0"}, + {file = "yarl-1.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4345f58719825bba29895011e8e3b545e6e00257abb984f9f27fe923afca2501"}, + {file = "yarl-1.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d9b980d7234614bc4674468ab173ed77d678349c860c3af83b1fffb6a837ddc"}, + {file = "yarl-1.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af4baa8a445977831cbaa91a9a84cc09debb10bc8391f128da2f7bd070fc351d"}, + {file = "yarl-1.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:123393db7420e71d6ce40d24885a9e65eb1edefc7a5228db2d62bcab3386a5c0"}, + {file = "yarl-1.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ab47acc9332f3de1b39e9b702d9c916af7f02656b2a86a474d9db4e53ef8fd7a"}, + {file = "yarl-1.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4a34c52ed158f89876cba9c600b2c964dfc1ca52ba7b3ab6deb722d1d8be6df2"}, + {file = "yarl-1.20.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:04d8cfb12714158abf2618f792c77bc5c3d8c5f37353e79509608be4f18705c9"}, + {file = "yarl-1.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7dc63ad0d541c38b6ae2255aaa794434293964677d5c1ec5d0116b0e308031f5"}, + {file = "yarl-1.20.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d02b591a64e4e6ca18c5e3d925f11b559c763b950184a64cf47d74d7e41877"}, + {file = "yarl-1.20.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:95fc9876f917cac7f757df80a5dda9de59d423568460fe75d128c813b9af558e"}, + {file = "yarl-1.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bb769ae5760cd1c6a712135ee7915f9d43f11d9ef769cb3f75a23e398a92d384"}, + {file = "yarl-1.20.0-cp313-cp313-win32.whl", hash = "sha256:70e0c580a0292c7414a1cead1e076c9786f685c1fc4757573d2967689b370e62"}, + {file = "yarl-1.20.0-cp313-cp313-win_amd64.whl", hash = "sha256:4c43030e4b0af775a85be1fa0433119b1565673266a70bf87ef68a9d5ba3174c"}, + {file = "yarl-1.20.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b6c4c3d0d6a0ae9b281e492b1465c72de433b782e6b5001c8e7249e085b69051"}, + {file = "yarl-1.20.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8681700f4e4df891eafa4f69a439a6e7d480d64e52bf460918f58e443bd3da7d"}, + {file = "yarl-1.20.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:84aeb556cb06c00652dbf87c17838eb6d92cfd317799a8092cee0e570ee11229"}, + {file = "yarl-1.20.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f166eafa78810ddb383e930d62e623d288fb04ec566d1b4790099ae0f31485f1"}, + {file = "yarl-1.20.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5d3d6d14754aefc7a458261027a562f024d4f6b8a798adb472277f675857b1eb"}, + {file = "yarl-1.20.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a8f64df8ed5d04c51260dbae3cc82e5649834eebea9eadfd829837b8093eb00"}, + {file = "yarl-1.20.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4d9949eaf05b4d30e93e4034a7790634bbb41b8be2d07edd26754f2e38e491de"}, + {file = "yarl-1.20.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c366b254082d21cc4f08f522ac201d0d83a8b8447ab562732931d31d80eb2a5"}, + {file = "yarl-1.20.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91bc450c80a2e9685b10e34e41aef3d44ddf99b3a498717938926d05ca493f6a"}, + {file = "yarl-1.20.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9c2aa4387de4bc3a5fe158080757748d16567119bef215bec643716b4fbf53f9"}, + {file = "yarl-1.20.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:d2cbca6760a541189cf87ee54ff891e1d9ea6406079c66341008f7ef6ab61145"}, + {file = "yarl-1.20.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:798a5074e656f06b9fad1a162be5a32da45237ce19d07884d0b67a0aa9d5fdda"}, + {file = "yarl-1.20.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:f106e75c454288472dbe615accef8248c686958c2e7dd3b8d8ee2669770d020f"}, + {file = "yarl-1.20.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:3b60a86551669c23dc5445010534d2c5d8a4e012163218fc9114e857c0586fdd"}, + {file = "yarl-1.20.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3e429857e341d5e8e15806118e0294f8073ba9c4580637e59ab7b238afca836f"}, + {file = "yarl-1.20.0-cp313-cp313t-win32.whl", hash = "sha256:65a4053580fe88a63e8e4056b427224cd01edfb5f951498bfefca4052f0ce0ac"}, + {file = "yarl-1.20.0-cp313-cp313t-win_amd64.whl", hash = "sha256:53b2da3a6ca0a541c1ae799c349788d480e5144cac47dba0266c7cb6c76151fe"}, + {file = "yarl-1.20.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:119bca25e63a7725b0c9d20ac67ca6d98fa40e5a894bd5d4686010ff73397914"}, + {file = "yarl-1.20.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:35d20fb919546995f1d8c9e41f485febd266f60e55383090010f272aca93edcc"}, + {file = "yarl-1.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:484e7a08f72683c0f160270566b4395ea5412b4359772b98659921411d32ad26"}, + {file = "yarl-1.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d8a3d54a090e0fff5837cd3cc305dd8a07d3435a088ddb1f65e33b322f66a94"}, + {file = "yarl-1.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f0cf05ae2d3d87a8c9022f3885ac6dea2b751aefd66a4f200e408a61ae9b7f0d"}, + {file = "yarl-1.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a884b8974729e3899d9287df46f015ce53f7282d8d3340fa0ed57536b440621c"}, + {file = "yarl-1.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f8d8aa8dd89ffb9a831fedbcb27d00ffd9f4842107d52dc9d57e64cb34073d5c"}, + {file = "yarl-1.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b4e88d6c3c8672f45a30867817e4537df1bbc6f882a91581faf1f6d9f0f1b5a"}, + {file = "yarl-1.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdb77efde644d6f1ad27be8a5d67c10b7f769804fff7a966ccb1da5a4de4b656"}, + {file = "yarl-1.20.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4ba5e59f14bfe8d261a654278a0f6364feef64a794bd456a8c9e823071e5061c"}, + {file = "yarl-1.20.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:d0bf955b96ea44ad914bc792c26a0edcd71b4668b93cbcd60f5b0aeaaed06c64"}, + {file = "yarl-1.20.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:27359776bc359ee6eaefe40cb19060238f31228799e43ebd3884e9c589e63b20"}, + {file = "yarl-1.20.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:04d9c7a1dc0a26efb33e1acb56c8849bd57a693b85f44774356c92d610369efa"}, + {file = "yarl-1.20.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:faa709b66ae0e24c8e5134033187a972d849d87ed0a12a0366bedcc6b5dc14a5"}, + {file = "yarl-1.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:44869ee8538208fe5d9342ed62c11cc6a7a1af1b3d0bb79bb795101b6e77f6e0"}, + {file = "yarl-1.20.0-cp39-cp39-win32.whl", hash = "sha256:b7fa0cb9fd27ffb1211cde944b41f5c67ab1c13a13ebafe470b1e206b8459da8"}, + {file = "yarl-1.20.0-cp39-cp39-win_amd64.whl", hash = "sha256:d4fad6e5189c847820288286732075f213eabf81be4d08d6cc309912e62be5b7"}, + {file = "yarl-1.20.0-py3-none-any.whl", hash = "sha256:5d0fe6af927a47a230f31e6004621fd0959eaa915fc62acfafa67ff7229a3124"}, + {file = "yarl-1.20.0.tar.gz", hash = "sha256:686d51e51ee5dfe62dec86e4866ee0e9ed66df700d55c828a615640adc885307"}, ] [package.dependencies] idna = ">=2.0" multidict = ">=4.0" -propcache = ">=0.2.0" +propcache = ">=0.2.1" [[package]] name = "zipp" @@ -1724,22 +1709,20 @@ version = "3.21.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" -groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version < \"3.10\"" files = [ {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] [metadata] -lock-version = "2.1" +lock-version = "2.0" python-versions = ">=3.9" -content-hash = "70bbc48156b21af9642d899dbbe0425c5a4dea1e4942ca9308280ebf5512f7b4" +content-hash = "d0fa0afe9566f613350ddf924fddc009d9bdc1e4b906ed1ad799710bd4f54e12" diff --git a/pyproject.toml b/pyproject.toml index f066f59..971d43c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ keywords = ["cloudfoundry", "cf"] [tool.poetry.dependencies] python = ">=3.9" aiohttp = ">=3.8.0" -protobuf = ">= 3.20.0, < 6.0.0" +protobuf = "6.31.0" oauth2-client= "1.4.2" websocket-client= "~1.8.0" PyYAML = ">=6.0" diff --git a/tests/v2/test_doppler.py b/tests/test_doppler.py similarity index 100% rename from tests/v2/test_doppler.py rename to tests/test_doppler.py diff --git a/vendors/dropsonde-protocol/envelope.proto b/vendors/dropsonde-protocol/envelope.proto index 4cdcd3c..1a978ba 100644 --- a/vendors/dropsonde-protocol/envelope.proto +++ b/vendors/dropsonde-protocol/envelope.proto @@ -1,5 +1,9 @@ syntax = "proto2"; + package events; + +option go_package = "github.com/cloudfoundry/sonde-go/events"; + option java_package = "org.cloudfoundry.dropsonde.events"; option java_outer_classname = "EventFactory"; @@ -8,13 +12,12 @@ import "log.proto"; import "metric.proto"; import "error.proto"; -/// Envelope wraps an Event and adds metadata. +// Envelope wraps an Event and adds metadata. message Envelope { - /// Type of the wrapped event. + // Type of the wrapped event. enum EventType { - // Removed Heartbeat at position 1 - // Removed HttpStart at position 2 - // Removed HttpStop at position 3 + reserved 1 to 3; + reserved "Heartbeat", "HttpStart", "HttpStop"; HttpStartStop = 4; LogMessage = 5; ValueMetric = 6; @@ -23,21 +26,34 @@ message Envelope { ContainerMetric = 9; } - required string origin = 1; /// Unique description of the origin of this event. - required EventType eventType = 2; /// Type of wrapped event. Only the optional field corresponding to the value of eventType should be set. + // Unique description of the origin of this event. + required string origin = 1; + + // Type of wrapped event. Only the optional field corresponding to the + // value of eventType should be set. + required EventType eventType = 2; + + // UNIX timestamp (in nanoseconds) event was wrapped in this Envelope. + optional int64 timestamp = 6; - optional int64 timestamp = 6; /// UNIX timestamp (in nanoseconds) event was wrapped in this Envelope. + // Deployment name (used to uniquely identify source). + optional string deployment = 13; - optional string deployment = 13; /// Deployment name (used to uniquely identify source). - optional string job = 14; /// Job name (used to uniquely identify source). - optional string index = 15; /// Index of job (used to uniquely identify source). - optional string ip = 16; /// IP address (used to uniquely identify source). + // Job name (used to uniquely identify source). + optional string job = 14; - map tags = 17; /// key/value tags to include additional identifying information. + // Index of job (used to uniquely identify source). + optional string index = 15; + + // IP address (used to uniquely identify source). + optional string ip = 16; + + // key/value tags to include additional identifying information. + map tags = 17; + + reserved 3 to 5; + reserved "Heartbeat", "HttpStart", "HttpStop"; - // Removed Heartbeat at position 3 - // Removed HttpStart at position 4 - // Removed HttpStop at position 5 optional HttpStartStop httpStartStop = 7; optional LogMessage logMessage = 8; optional ValueMetric valueMetric = 9; @@ -45,4 +61,3 @@ message Envelope { optional Error error = 11; optional ContainerMetric containerMetric = 12; } - diff --git a/vendors/dropsonde-protocol/error.proto b/vendors/dropsonde-protocol/error.proto index fa13a0f..9e64db3 100644 --- a/vendors/dropsonde-protocol/error.proto +++ b/vendors/dropsonde-protocol/error.proto @@ -1,12 +1,22 @@ syntax = "proto2"; + package events; +option go_package = "github.com/cloudfoundry/sonde-go/events"; + option java_package = "org.cloudfoundry.dropsonde.events"; option java_outer_classname = "ErrorFactory"; -/// An Error event represents an error in the originating process. +// An Error event represents an error in the originating process. message Error { - required string source = 1; /// Source of the error. This may or may not be the same as the Origin in the envelope. - required int32 code = 2; /// Numeric error code. This is provided for programmatic responses to the error. - required string message = 3; /// Error description (preferably human-readable). + // Source of the error. This may or may not be the same as the Origin in + // the envelope. + required string source = 1; + + // Numeric error code. This is provided for programmatic responses to the + // error. + required int32 code = 2; + + // Error description (preferably human-readable). + required string message = 3; } diff --git a/vendors/dropsonde-protocol/http.proto b/vendors/dropsonde-protocol/http.proto index d6b0a0b..2ec17d7 100644 --- a/vendors/dropsonde-protocol/http.proto +++ b/vendors/dropsonde-protocol/http.proto @@ -1,18 +1,24 @@ syntax = "proto2"; + package events; +option go_package = "github.com/cloudfoundry/sonde-go/events"; + option java_package = "org.cloudfoundry.dropsonde.events"; option java_outer_classname = "HttpFactory"; import "uuid.proto"; -/// Type of peer handling request. +// Type of peer handling request. enum PeerType { - Client = 1; /// Request is made by this process. - Server = 2; /// Request is received by this process. + // Request is made by this process. + Client = 1; + + // Request is received by this process. + Server = 2; } -/// HTTP method. +// HTTP method. enum Method { GET = 1; POST = 2; @@ -61,25 +67,54 @@ enum Method { VERSION_CONTROL = 44; } -/// An HttpStartStop event represents the whole lifecycle of an HTTP request. +// An HttpStartStop event represents the whole lifecycle of an HTTP request. message HttpStartStop { - required int64 startTimestamp = 1; /// UNIX timestamp (in nanoseconds) when the request was sent (by a client) or received (by a server). - required int64 stopTimestamp = 2; /// UNIX timestamp (in nanoseconds) when the request was received. + // UNIX timestamp (in nanoseconds) when the request was sent (by a client) + // or received (by a server). + required int64 startTimestamp = 1; + + // UNIX timestamp (in nanoseconds) when the request was received. + required int64 stopTimestamp = 2; + + // ID for tracking lifecycle of request. + required UUID requestId = 3; + + // Role of the emitting process in the request cycle. + required PeerType peerType = 4; + + // Method of the request. + required Method method = 5; + + // Destination of the request. + required string uri = 6; + + // Remote address of the request. (For a server, this should be the origin + // of the request.) + required string remoteAddress = 7; + + // Contents of the UserAgent header on the request. + required string userAgent = 8; + + // Status code returned with the response to the request. + required int32 statusCode = 9; + + // Length of response (bytes). + required int64 contentLength = 10; + + reserved 11; + reserved "parentRequestId"; - required UUID requestId = 3; /// ID for tracking lifecycle of request. - required PeerType peerType = 4; /// Role of the emitting process in the request cycle. - required Method method = 5; /// Method of the request. - required string uri = 6; /// Destination of the request. - required string remoteAddress = 7; /// Remote address of the request. (For a server, this should be the origin of the request.) - required string userAgent = 8; /// Contents of the UserAgent header on the request. + // If this request was made in relation to an appliciation, this field + // should track that application's ID. + optional UUID applicationId = 12; - required int32 statusCode = 9; /// Status code returned with the response to the request. - required int64 contentLength = 10; /// Length of response (bytes). + // Index of the application instance. + optional int32 instanceIndex = 13; - /// 11 used to be ParentRequestID which has been deprecated. + // ID of the application instance. + optional string instanceId = 14; - optional UUID applicationId = 12; /// If this request was made in relation to an appliciation, this field should track that application's ID. - optional int32 instanceIndex = 13; /// Index of the application instance. - optional string instanceId = 14; /// ID of the application instance. - repeated string forwarded = 15; /// This contains http forwarded-for [x-forwarded-for] header from the request. + // This contains http forwarded-for [x-forwarded-for] header from the + // request. + repeated string forwarded = 15; } diff --git a/vendors/dropsonde-protocol/log.proto b/vendors/dropsonde-protocol/log.proto index 0882792..2174a2e 100644 --- a/vendors/dropsonde-protocol/log.proto +++ b/vendors/dropsonde-protocol/log.proto @@ -1,22 +1,35 @@ syntax = "proto2"; + package events; +option go_package = "github.com/cloudfoundry/sonde-go/events"; + option java_package = "org.cloudfoundry.dropsonde.events"; option java_outer_classname = "LogFactory"; -/// A LogMessage contains a "log line" and associated metadata. +// A LogMessage contains a "log line" and associated metadata. message LogMessage { - - /// MessageType stores the destination of the message (corresponding to STDOUT or STDERR). + // MessageType stores the destination of the message (corresponding to STDOUT or STDERR). enum MessageType { OUT = 1; ERR = 2; } - required bytes message = 1; /// Bytes of the log message. (Note that it is not required to be a single line.) - required MessageType message_type = 2; /// Type of the message (OUT or ERR). - required int64 timestamp = 3; /// UNIX timestamp (in nanoseconds) when the log was written. - optional string app_id = 4; /// Application that emitted the message (or to which the application is related). - optional string source_type = 5; /// Source of the message. For Cloud Foundry, this can be "APP", "RTR", "DEA", "STG", etc. - optional string source_instance = 6; /// Instance that emitted the message. + // Bytes of the log message. (Note that it is not required to be a single line.) + required bytes message = 1; + + // Type of the message (OUT or ERR). + required MessageType message_type = 2; + + // UNIX timestamp (in nanoseconds) when the log was written. + required int64 timestamp = 3; + + // Application that emitted the message (or to which the application is related). + optional string app_id = 4; + + // Source of the message. For Cloud Foundry, this can be "APP", "RTR", "DEA", "STG", etc. + optional string source_type = 5; + + // Instance that emitted the message. + optional string source_instance = 6; } diff --git a/vendors/dropsonde-protocol/metric.proto b/vendors/dropsonde-protocol/metric.proto index 1d1fce0..d6fead9 100644 --- a/vendors/dropsonde-protocol/metric.proto +++ b/vendors/dropsonde-protocol/metric.proto @@ -1,31 +1,65 @@ syntax = "proto2"; + package events; +option go_package = "github.com/cloudfoundry/sonde-go/events"; + option java_package = "org.cloudfoundry.dropsonde.events"; option java_outer_classname = "MetricFactory"; -/// A ValueMetric indicates the value of a metric at an instant in time. +// A ValueMetric indicates the value of a metric at an instant in time. message ValueMetric { - required string name = 1; /// Name of the metric. Must be consistent for downstream consumers to associate events semantically. - required double value = 2; /// Value at the time of event emission. - required string unit = 3; /// Unit of the metric. Please see http://metrics20.org/spec/#units for ideas; SI units/prefixes are recommended where applicable. Should be consistent for the life of the metric (consumers are expected to report, but not interpret, prefixes). + // Name of the metric. Must be consistent for downstream consumers to + // associate events semantically. + required string name = 1; + + // Value at the time of event emission. + required double value = 2; + + // Unit of the metric. Please see http://metrics20.org/spec/#units for + // ideas; SI units/prefixes are recommended where applicable. Should be + // consistent for the life of the metric (consumers are expected to report, + // but not interpret, prefixes). + required string unit = 3; } -/// A CounterEvent represents the increment of a counter. It contains only the change in the value; it is the responsibility of downstream consumers to maintain the value of the counter. +// A CounterEvent represents the increment of a counter. It contains only the +// change in the value; it is the responsibility of downstream consumers to +// maintain the value of the counter. message CounterEvent { - required string name = 1; /// Name of the counter. Must be consistent for downstream consumers to associate events semantically. - required uint64 delta = 2; /// Amount by which to increment the counter. - optional uint64 total = 3; /// Total value of the counter. This will be overridden by Metron, which internally tracks the total of each named Counter it receives. + // Name of the counter. Must be consistent for downstream consumers to + // associate events semantically. + required string name = 1; + + // Amount by which to increment the counter. + required uint64 delta = 2; + + // Total value of the counter. This will be overridden by Metron, which + // internally tracks the total of each named Counter it receives. + optional uint64 total = 3; } -/// A ContainerMetric records resource usage of an app in a container. +// A ContainerMetric records resource usage of an app in a container. message ContainerMetric { - required string applicationId = 1; /// ID of the contained application. - required int32 instanceIndex = 2; /// Instance index of the contained application. (This, with applicationId, should uniquely identify a container.) - - required double cpuPercentage = 3; /// CPU based on number of cores. - required uint64 memoryBytes = 4; /// Bytes of memory used. - required uint64 diskBytes = 5; /// Bytes of disk used. - optional uint64 memoryBytesQuota = 6; /// Maximum bytes of memory allocated to container. - optional uint64 diskBytesQuota = 7; /// Maximum bytes of disk allocated to container. + // ID of the contained application. + required string applicationId = 1; + + // Instance index of the contained application. (This, with applicationId, + // should uniquely identify a container.) + required int32 instanceIndex = 2; + + // CPU based on number of cores. + required double cpuPercentage = 3; + + // Bytes of memory used. + required uint64 memoryBytes = 4; + + // Bytes of disk used. + required uint64 diskBytes = 5; + + // Maximum bytes of memory allocated to container. + optional uint64 memoryBytesQuota = 6; + + // Maximum bytes of disk allocated to container. + optional uint64 diskBytesQuota = 7; } diff --git a/vendors/dropsonde-protocol/uuid.proto b/vendors/dropsonde-protocol/uuid.proto index 826a51e..686155d 100644 --- a/vendors/dropsonde-protocol/uuid.proto +++ b/vendors/dropsonde-protocol/uuid.proto @@ -1,12 +1,17 @@ syntax = "proto2"; + package events; +option go_package = "github.com/cloudfoundry/sonde-go/events"; + option java_package = "org.cloudfoundry.dropsonde.events"; option java_outer_classname = "UuidFactory"; -/// Type representing a 128-bit UUID. +// Type representing a 128-bit UUID. // -// The bytes of the UUID should be packed in little-endian **byte** (not bit) order. For example, the UUID `f47ac10b-58cc-4372-a567-0e02b2c3d479` should be encoded as `UUID{ low: 0x7243cc580bc17af4, high: 0x79d4c3b2020e67a5 }` +// The bytes of the UUID should be packed in little-endian **byte** (not bit) +// order. For example, the UUID `f47ac10b-58cc-4372-a567-0e02b2c3d479` should +// be encoded as `UUID{ low: 0x7243cc580bc17af4, high: 0x79d4c3b2020e67a5 }` message UUID { required uint64 low = 1; required uint64 high = 2; From 6880dfa7b3543ea6b89257b11b2dcbfac9c75d91 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 18:36:21 +0200 Subject: [PATCH 197/264] Bump protobuf from 6.31.0 to 6.31.1 (#239) Bumps [protobuf](https://github.com/protocolbuffers/protobuf) from 6.31.0 to 6.31.1. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Changelog](https://github.com/protocolbuffers/protobuf/blob/main/protobuf_release.bzl) - [Commits](https://github.com/protocolbuffers/protobuf/compare/v6.31.0...v6.31.1) --- updated-dependencies: - dependency-name: protobuf dependency-version: 6.31.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 136 +++++++++++++++++++++++++++++++++++++++---------- pyproject.toml | 2 +- 2 files changed, 109 insertions(+), 29 deletions(-) diff --git a/poetry.lock b/poetry.lock index a264769..7de3ee2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -6,6 +6,7 @@ version = "2.6.1" description = "Happy Eyeballs for asyncio" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8"}, {file = "aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558"}, @@ -17,6 +18,7 @@ version = "3.11.18" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "aiohttp-3.11.18-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:96264854fedbea933a9ca4b7e0c745728f01380691687b7365d18d9e977179c4"}, {file = "aiohttp-3.11.18-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9602044ff047043430452bc3a2089743fa85da829e6fc9ee0025351d66c332b6"}, @@ -112,7 +114,7 @@ propcache = ">=0.2.0" yarl = ">=1.17.0,<2.0" [package.extras] -speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] +speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.2.0) ; sys_platform == \"linux\" or sys_platform == \"darwin\"", "brotlicffi ; platform_python_implementation != \"CPython\""] [[package]] name = "aiosignal" @@ -120,6 +122,7 @@ version = "1.3.2" description = "aiosignal: a list of registered asynchronous callbacks" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, @@ -134,6 +137,8 @@ version = "5.0.1" description = "Timeout context manager for asyncio programs" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version < \"3.11\"" files = [ {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, @@ -145,18 +150,19 @@ version = "25.3.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, ] [package.extras] -benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] -tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] +tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] [[package]] name = "backports-tarfile" @@ -164,6 +170,8 @@ version = "1.2.0" description = "Backport of CPython tarfile module" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\"" files = [ {file = "backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34"}, {file = "backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991"}, @@ -179,6 +187,7 @@ version = "25.1.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32"}, {file = "black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da"}, @@ -225,6 +234,7 @@ version = "2025.4.26" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" +groups = ["main", "dev"] files = [ {file = "certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3"}, {file = "certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6"}, @@ -236,6 +246,8 @@ version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\"" files = [ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, @@ -315,6 +327,7 @@ version = "3.4.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"}, {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"}, @@ -416,6 +429,7 @@ version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -430,6 +444,8 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +markers = "platform_system == \"Windows\" or sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -441,6 +457,8 @@ version = "43.0.3" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" files = [ {file = "cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e"}, {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e"}, @@ -490,6 +508,7 @@ version = "0.21.2" description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, @@ -501,6 +520,8 @@ version = "1.3.0" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "python_version < \"3.11\"" files = [ {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, @@ -518,6 +539,7 @@ version = "7.2.0" description = "the modular source code checker: pep8 pyflakes and co" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "flake8-7.2.0-py2.py3-none-any.whl", hash = "sha256:93b92ba5bdb60754a6da14fa3b93a9361fd00a59632ada61fd7b130436c40343"}, {file = "flake8-7.2.0.tar.gz", hash = "sha256:fa558ae3f6f7dbf2b4f22663e5343b6b6023620461f8d4ff2019ef4b5ee70426"}, @@ -534,6 +556,7 @@ version = "1.6.0" description = "A list-like structure which implements collections.abc.MutableSequence" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "frozenlist-1.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e6e558ea1e47fd6fa8ac9ccdad403e5dd5ecc6ed8dda94343056fa4277d5c65e"}, {file = "frozenlist-1.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f4b3cd7334a4bbc0c472164f3744562cb72d05002cc6fcf58adb104630bbc352"}, @@ -647,6 +670,7 @@ version = "1.5.0" description = "A tool for generating OIDC identities" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "id-1.5.0-py3-none-any.whl", hash = "sha256:f1434e1cef91f2cbb8a4ec64663d5a23b9ed43ef44c4c957d02583d61714c658"}, {file = "id-1.5.0.tar.gz", hash = "sha256:292cb8a49eacbbdbce97244f47a97b4c62540169c976552e497fd57df0734c1d"}, @@ -666,6 +690,7 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" +groups = ["main", "dev"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -680,6 +705,8 @@ version = "8.7.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.9" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version < \"3.10\"" files = [ {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"}, {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"}, @@ -689,12 +716,12 @@ files = [ zipp = ">=3.20" [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib_resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] [[package]] @@ -703,6 +730,7 @@ version = "2.1.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, @@ -714,6 +742,8 @@ version = "3.4.0" description = "Utility functions for Python class constructs" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" files = [ {file = "jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790"}, {file = "jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd"}, @@ -732,6 +762,8 @@ version = "6.0.1" description = "Useful decorators and context managers" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" files = [ {file = "jaraco.context-6.0.1-py3-none-any.whl", hash = "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4"}, {file = "jaraco_context-6.0.1.tar.gz", hash = "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3"}, @@ -742,7 +774,7 @@ files = [ [package.extras] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -test = ["portend", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"] +test = ["portend", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] [[package]] name = "jaraco-functools" @@ -750,6 +782,8 @@ version = "4.1.0" description = "Functools like those found in stdlib" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" files = [ {file = "jaraco.functools-4.1.0-py3-none-any.whl", hash = "sha256:ad159f13428bc4acbf5541ad6dec511f91573b90fba04df61dafa2a1231cf649"}, {file = "jaraco_functools-4.1.0.tar.gz", hash = "sha256:70f7e0e2ae076498e212562325e805204fc092d7b4c17e0e86c959e249701a9d"}, @@ -759,7 +793,7 @@ files = [ more-itertools = "*" [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] @@ -772,13 +806,15 @@ version = "0.9.0" description = "Low-level, pure Python DBus protocol wrapper." optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" files = [ {file = "jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683"}, {file = "jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732"}, ] [package.extras] -test = ["async-timeout", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"] +test = ["async-timeout ; python_version < \"3.11\"", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"] trio = ["trio"] [[package]] @@ -787,6 +823,8 @@ version = "25.6.0" description = "Store and access your passwords safely." optional = false python-versions = ">=3.9" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" files = [ {file = "keyring-25.6.0-py3-none-any.whl", hash = "sha256:552a3f7af126ece7ed5c89753650eec89c7eaae8617d0aa4d9ad2b75111266bd"}, {file = "keyring-25.6.0.tar.gz", hash = "sha256:0b39998aa941431eb3d9b0d4b2460bc773b9df6fed7621c2dfb291a7e0187a66"}, @@ -802,7 +840,7 @@ pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""} SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] completion = ["shtab (>=1.1.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] @@ -816,6 +854,7 @@ version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, @@ -840,6 +879,7 @@ version = "0.7.0" description = "McCabe checker, plugin for flake8" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, @@ -851,6 +891,7 @@ version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, @@ -862,6 +903,8 @@ version = "10.7.0" description = "More routines for operating on iterables, beyond itertools" optional = false python-versions = ">=3.9" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" files = [ {file = "more_itertools-10.7.0-py3-none-any.whl", hash = "sha256:d43980384673cb07d2f7d2d918c616b30c659c089ee23953f601d6609c67510e"}, {file = "more_itertools-10.7.0.tar.gz", hash = "sha256:9fddd5403be01a94b204faadcff459ec3568cf110265d3c54323e1e866ad29d3"}, @@ -873,6 +916,7 @@ version = "6.4.3" description = "multidict implementation" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "multidict-6.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:32a998bd8a64ca48616eac5a8c1cc4fa38fb244a3facf2eeb14abe186e0f6cc5"}, {file = "multidict-6.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a54ec568f1fc7f3c313c2f3b16e5db346bf3660e1309746e7fccbbfded856188"}, @@ -989,6 +1033,7 @@ version = "1.1.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, @@ -1000,6 +1045,7 @@ version = "0.2.21" description = "Python binding to Ammonia HTML sanitizer Rust crate" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "nh3-0.2.21-cp313-cp313t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:fcff321bd60c6c5c9cb4ddf2554e22772bb41ebd93ad88171bbbb6f271255286"}, {file = "nh3-0.2.21-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31eedcd7d08b0eae28ba47f43fd33a653b4cdb271d64f1aeda47001618348fde"}, @@ -1033,6 +1079,7 @@ version = "1.4.2" description = "A client library for OAuth2" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "oauth2-client-1.4.2.tar.gz", hash = "sha256:5381900448ff1ae762eb7c65c501002eac46bb5ca2f49477fdfeaf9e9969f284"}, {file = "oauth2_client-1.4.2-py3-none-any.whl", hash = "sha256:7b938ba8166128a3c4c15ad23ca0c95a2468f8e8b6069d019ebc73360c15c7ca"}, @@ -1047,6 +1094,7 @@ version = "25.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, @@ -1058,6 +1106,7 @@ version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -1069,6 +1118,7 @@ version = "4.3.8" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, @@ -1085,6 +1135,7 @@ version = "1.6.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, @@ -1100,6 +1151,7 @@ version = "0.5.0" description = "Updated polling utility with many configurable options" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "polling2-0.5.0-py2.py3-none-any.whl", hash = "sha256:ad86d56fbd7502f0856cac2d0109d595c18fa6c7fb12c88cee5e5d16c17286c1"}, {file = "polling2-0.5.0.tar.gz", hash = "sha256:90b7da82cf7adbb48029724d3546af93f21ab6e592ec37c8c4619aedd010e342"}, @@ -1111,6 +1163,7 @@ version = "0.3.1" description = "Accelerated property cache" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "propcache-0.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f27785888d2fdd918bc36de8b8739f2d6c791399552333721b58193f68ea3e98"}, {file = "propcache-0.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4e89cde74154c7b5957f87a355bb9c8ec929c167b59c83d90654ea36aeb6180"}, @@ -1214,20 +1267,21 @@ files = [ [[package]] name = "protobuf" -version = "6.31.0" +version = "6.31.1" description = "" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ - {file = "protobuf-6.31.0-cp310-abi3-win32.whl", hash = "sha256:10bd62802dfa0588649740a59354090eaf54b8322f772fbdcca19bc78d27f0d6"}, - {file = "protobuf-6.31.0-cp310-abi3-win_amd64.whl", hash = "sha256:3e987c99fd634be8347246a02123250f394ba20573c953de133dc8b2c107dd71"}, - {file = "protobuf-6.31.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:2c812f0f96ceb6b514448cefeb1df54ec06dde456783f5099c0e2f8a0f2caa89"}, - {file = "protobuf-6.31.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:67ce50195e4e584275623b8e6bc6d3d3dfd93924bf6116b86b3b8975ab9e4571"}, - {file = "protobuf-6.31.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:5353e38844168a327acd2b2aa440044411cd8d1b6774d5701008bd1dba067c79"}, - {file = "protobuf-6.31.0-cp39-cp39-win32.whl", hash = "sha256:96d8da25c83b11db5fe9e0376351ce25e7205e13224d939e097b6f82a72af824"}, - {file = "protobuf-6.31.0-cp39-cp39-win_amd64.whl", hash = "sha256:00a873c06efdfb854145d9ded730b09cf57d206075c38132674093370e2edabb"}, - {file = "protobuf-6.31.0-py3-none-any.whl", hash = "sha256:6ac2e82556e822c17a8d23aa1190bbc1d06efb9c261981da95c71c9da09e9e23"}, - {file = "protobuf-6.31.0.tar.gz", hash = "sha256:314fab1a6a316469dc2dd46f993cbbe95c861ea6807da910becfe7475bc26ffe"}, + {file = "protobuf-6.31.1-cp310-abi3-win32.whl", hash = "sha256:7fa17d5a29c2e04b7d90e5e32388b8bfd0e7107cd8e616feef7ed3fa6bdab5c9"}, + {file = "protobuf-6.31.1-cp310-abi3-win_amd64.whl", hash = "sha256:426f59d2964864a1a366254fa703b8632dcec0790d8862d30034d8245e1cd447"}, + {file = "protobuf-6.31.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:6f1227473dc43d44ed644425268eb7c2e488ae245d51c6866d19fe158e207402"}, + {file = "protobuf-6.31.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:a40fc12b84c154884d7d4c4ebd675d5b3b5283e155f324049ae396b95ddebc39"}, + {file = "protobuf-6.31.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4ee898bf66f7a8b0bd21bce523814e6fbd8c6add948045ce958b73af7e8878c6"}, + {file = "protobuf-6.31.1-cp39-cp39-win32.whl", hash = "sha256:0414e3aa5a5f3ff423828e1e6a6e907d6c65c1d5b7e6e975793d5590bdeecc16"}, + {file = "protobuf-6.31.1-cp39-cp39-win_amd64.whl", hash = "sha256:8764cf4587791e7564051b35524b72844f845ad0bb011704c3736cce762d8fe9"}, + {file = "protobuf-6.31.1-py3-none-any.whl", hash = "sha256:720a6c7e6b77288b85063569baae8536671b39f15cc22037ec7045658d80489e"}, + {file = "protobuf-6.31.1.tar.gz", hash = "sha256:d8cac4c982f0b957a4dc73a80e2ea24fab08e679c0de9deb835f4a12d69aca9a"}, ] [[package]] @@ -1236,6 +1290,7 @@ version = "2.13.0" description = "Python style guide checker" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "pycodestyle-2.13.0-py2.py3-none-any.whl", hash = "sha256:35863c5974a271c7a726ed228a14a4f6daf49df369d8c50cd9a6f58a5e143ba9"}, {file = "pycodestyle-2.13.0.tar.gz", hash = "sha256:c8415bf09abe81d9c7f872502a6eee881fbe85d8763dd5b9924bb0a01d67efae"}, @@ -1247,6 +1302,8 @@ version = "2.22" description = "C parser in Python" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\"" files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, @@ -1258,6 +1315,7 @@ version = "3.3.2" description = "passive checker of Python programs" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "pyflakes-3.3.2-py2.py3-none-any.whl", hash = "sha256:5039c8339cbb1944045f4ee5466908906180f13cc99cc9949348d10f82a5c32a"}, {file = "pyflakes-3.3.2.tar.gz", hash = "sha256:6dfd61d87b97fba5dcfaaf781171ac16be16453be6d816147989e7f6e6a9576b"}, @@ -1269,6 +1327,7 @@ version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, @@ -1283,6 +1342,7 @@ version = "8.3.5" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, @@ -1305,6 +1365,8 @@ version = "0.2.3" description = "A (partial) reimplementation of pywin32 using ctypes/cffi" optional = false python-versions = ">=3.6" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"win32\"" files = [ {file = "pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755"}, {file = "pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8"}, @@ -1316,6 +1378,7 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -1378,6 +1441,7 @@ version = "44.0" description = "readme_renderer is a library for rendering readme descriptions for Warehouse" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "readme_renderer-44.0-py3-none-any.whl", hash = "sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151"}, {file = "readme_renderer-44.0.tar.gz", hash = "sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1"}, @@ -1397,6 +1461,7 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -1418,6 +1483,7 @@ version = "1.0.0" description = "A utility belt for advanced users of python-requests" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["dev"] files = [ {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, @@ -1432,6 +1498,7 @@ version = "2.0.0" description = "Validating URI References per RFC 3986" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd"}, {file = "rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c"}, @@ -1446,6 +1513,7 @@ version = "14.0.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" +groups = ["dev"] files = [ {file = "rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0"}, {file = "rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725"}, @@ -1465,6 +1533,8 @@ version = "3.3.3" description = "Python bindings to FreeDesktop.org Secret Service API" optional = false python-versions = ">=3.6" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" files = [ {file = "SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"}, {file = "SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"}, @@ -1480,6 +1550,8 @@ version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version < \"3.11\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, @@ -1521,6 +1593,7 @@ version = "6.1.0" description = "Collection of utilities for publishing packages on PyPI" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "twine-6.1.0-py3-none-any.whl", hash = "sha256:a47f973caf122930bf0fbbf17f80b83bc1602c9ce393c7845f289a3001dc5384"}, {file = "twine-6.1.0.tar.gz", hash = "sha256:be324f6272eff91d07ee93f251edf232fc647935dd585ac003539b42404a8dbd"}, @@ -1547,6 +1620,8 @@ version = "4.13.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] +markers = "python_version < \"3.11\"" files = [ {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, @@ -1558,13 +1633,14 @@ version = "2.4.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" +groups = ["main", "dev"] files = [ {file = "urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813"}, {file = "urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -1575,6 +1651,7 @@ version = "1.8.0" description = "WebSocket client for Python with low level API options" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526"}, {file = "websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da"}, @@ -1591,6 +1668,7 @@ version = "1.20.0" description = "Yet another URL library" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "yarl-1.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f1f6670b9ae3daedb325fa55fbe31c22c8228f6e0b513772c2e1c623caa6ab22"}, {file = "yarl-1.20.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85a231fa250dfa3308f3c7896cc007a47bc76e9e8e8595c20b7426cac4884c62"}, @@ -1709,20 +1787,22 @@ version = "3.21.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" +groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version < \"3.10\"" files = [ {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = ">=3.9" -content-hash = "d0fa0afe9566f613350ddf924fddc009d9bdc1e4b906ed1ad799710bd4f54e12" +content-hash = "5cb2cca4213b415b57ffc5cb3214d425952521e0ce5dde7f07efc57e21e0b831" diff --git a/pyproject.toml b/pyproject.toml index 971d43c..c51794e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ keywords = ["cloudfoundry", "cf"] [tool.poetry.dependencies] python = ">=3.9" aiohttp = ">=3.8.0" -protobuf = "6.31.0" +protobuf = "6.31.1" oauth2-client= "1.4.2" websocket-client= "~1.8.0" PyYAML = ">=6.0" From 1a04c5fdf2d01ce152e45dbd454cb8765eeae934 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 18:41:17 +0200 Subject: [PATCH 198/264] Bump aiohttp from 3.11.18 to 3.12.6 (#240) --- updated-dependencies: - dependency-name: aiohttp dependency-version: 3.12.6 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 173 +++++++++++++++++++++++++++------------------------- 1 file changed, 89 insertions(+), 84 deletions(-) diff --git a/poetry.lock b/poetry.lock index 7de3ee2..1314725 100644 --- a/poetry.lock +++ b/poetry.lock @@ -14,97 +14,102 @@ files = [ [[package]] name = "aiohttp" -version = "3.11.18" +version = "3.12.13" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "aiohttp-3.11.18-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:96264854fedbea933a9ca4b7e0c745728f01380691687b7365d18d9e977179c4"}, - {file = "aiohttp-3.11.18-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9602044ff047043430452bc3a2089743fa85da829e6fc9ee0025351d66c332b6"}, - {file = "aiohttp-3.11.18-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5691dc38750fcb96a33ceef89642f139aa315c8a193bbd42a0c33476fd4a1609"}, - {file = "aiohttp-3.11.18-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:554c918ec43f8480b47a5ca758e10e793bd7410b83701676a4782672d670da55"}, - {file = "aiohttp-3.11.18-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a4076a2b3ba5b004b8cffca6afe18a3b2c5c9ef679b4d1e9859cf76295f8d4f"}, - {file = "aiohttp-3.11.18-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:767a97e6900edd11c762be96d82d13a1d7c4fc4b329f054e88b57cdc21fded94"}, - {file = "aiohttp-3.11.18-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0ddc9337a0fb0e727785ad4f41163cc314376e82b31846d3835673786420ef1"}, - {file = "aiohttp-3.11.18-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f414f37b244f2a97e79b98d48c5ff0789a0b4b4609b17d64fa81771ad780e415"}, - {file = "aiohttp-3.11.18-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fdb239f47328581e2ec7744ab5911f97afb10752332a6dd3d98e14e429e1a9e7"}, - {file = "aiohttp-3.11.18-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f2c50bad73ed629cc326cc0f75aed8ecfb013f88c5af116f33df556ed47143eb"}, - {file = "aiohttp-3.11.18-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a8d8f20c39d3fa84d1c28cdb97f3111387e48209e224408e75f29c6f8e0861d"}, - {file = "aiohttp-3.11.18-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:106032eaf9e62fd6bc6578c8b9e6dc4f5ed9a5c1c7fb2231010a1b4304393421"}, - {file = "aiohttp-3.11.18-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:b491e42183e8fcc9901d8dcd8ae644ff785590f1727f76ca86e731c61bfe6643"}, - {file = "aiohttp-3.11.18-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ad8c745ff9460a16b710e58e06a9dec11ebc0d8f4dd82091cefb579844d69868"}, - {file = "aiohttp-3.11.18-cp310-cp310-win32.whl", hash = "sha256:8e57da93e24303a883146510a434f0faf2f1e7e659f3041abc4e3fb3f6702a9f"}, - {file = "aiohttp-3.11.18-cp310-cp310-win_amd64.whl", hash = "sha256:cc93a4121d87d9f12739fc8fab0a95f78444e571ed63e40bfc78cd5abe700ac9"}, - {file = "aiohttp-3.11.18-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:427fdc56ccb6901ff8088544bde47084845ea81591deb16f957897f0f0ba1be9"}, - {file = "aiohttp-3.11.18-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c828b6d23b984255b85b9b04a5b963a74278b7356a7de84fda5e3b76866597b"}, - {file = "aiohttp-3.11.18-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5c2eaa145bb36b33af1ff2860820ba0589e165be4ab63a49aebfd0981c173b66"}, - {file = "aiohttp-3.11.18-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d518ce32179f7e2096bf4e3e8438cf445f05fedd597f252de9f54c728574756"}, - {file = "aiohttp-3.11.18-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0700055a6e05c2f4711011a44364020d7a10fbbcd02fbf3e30e8f7e7fddc8717"}, - {file = "aiohttp-3.11.18-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8bd1cde83e4684324e6ee19adfc25fd649d04078179890be7b29f76b501de8e4"}, - {file = "aiohttp-3.11.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73b8870fe1c9a201b8c0d12c94fe781b918664766728783241a79e0468427e4f"}, - {file = "aiohttp-3.11.18-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25557982dd36b9e32c0a3357f30804e80790ec2c4d20ac6bcc598533e04c6361"}, - {file = "aiohttp-3.11.18-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7e889c9df381a2433802991288a61e5a19ceb4f61bd14f5c9fa165655dcb1fd1"}, - {file = "aiohttp-3.11.18-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9ea345fda05bae217b6cce2acf3682ce3b13d0d16dd47d0de7080e5e21362421"}, - {file = "aiohttp-3.11.18-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9f26545b9940c4b46f0a9388fd04ee3ad7064c4017b5a334dd450f616396590e"}, - {file = "aiohttp-3.11.18-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3a621d85e85dccabd700294494d7179ed1590b6d07a35709bb9bd608c7f5dd1d"}, - {file = "aiohttp-3.11.18-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9c23fd8d08eb9c2af3faeedc8c56e134acdaf36e2117ee059d7defa655130e5f"}, - {file = "aiohttp-3.11.18-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9e6b0e519067caa4fd7fb72e3e8002d16a68e84e62e7291092a5433763dc0dd"}, - {file = "aiohttp-3.11.18-cp311-cp311-win32.whl", hash = "sha256:122f3e739f6607e5e4c6a2f8562a6f476192a682a52bda8b4c6d4254e1138f4d"}, - {file = "aiohttp-3.11.18-cp311-cp311-win_amd64.whl", hash = "sha256:e6f3c0a3a1e73e88af384b2e8a0b9f4fb73245afd47589df2afcab6b638fa0e6"}, - {file = "aiohttp-3.11.18-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:63d71eceb9cad35d47d71f78edac41fcd01ff10cacaa64e473d1aec13fa02df2"}, - {file = "aiohttp-3.11.18-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d1929da615840969929e8878d7951b31afe0bac883d84418f92e5755d7b49508"}, - {file = "aiohttp-3.11.18-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d0aebeb2392f19b184e3fdd9e651b0e39cd0f195cdb93328bd124a1d455cd0e"}, - {file = "aiohttp-3.11.18-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3849ead845e8444f7331c284132ab314b4dac43bfae1e3cf350906d4fff4620f"}, - {file = "aiohttp-3.11.18-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e8452ad6b2863709f8b3d615955aa0807bc093c34b8e25b3b52097fe421cb7f"}, - {file = "aiohttp-3.11.18-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b8d2b42073611c860a37f718b3d61ae8b4c2b124b2e776e2c10619d920350ec"}, - {file = "aiohttp-3.11.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40fbf91f6a0ac317c0a07eb328a1384941872f6761f2e6f7208b63c4cc0a7ff6"}, - {file = "aiohttp-3.11.18-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ff5625413fec55216da5eaa011cf6b0a2ed67a565914a212a51aa3755b0009"}, - {file = "aiohttp-3.11.18-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7f33a92a2fde08e8c6b0c61815521324fc1612f397abf96eed86b8e31618fdb4"}, - {file = "aiohttp-3.11.18-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:11d5391946605f445ddafda5eab11caf310f90cdda1fd99865564e3164f5cff9"}, - {file = "aiohttp-3.11.18-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3cc314245deb311364884e44242e00c18b5896e4fe6d5f942e7ad7e4cb640adb"}, - {file = "aiohttp-3.11.18-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0f421843b0f70740772228b9e8093289924359d306530bcd3926f39acbe1adda"}, - {file = "aiohttp-3.11.18-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e220e7562467dc8d589e31c1acd13438d82c03d7f385c9cd41a3f6d1d15807c1"}, - {file = "aiohttp-3.11.18-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ab2ef72f8605046115bc9aa8e9d14fd49086d405855f40b79ed9e5c1f9f4faea"}, - {file = "aiohttp-3.11.18-cp312-cp312-win32.whl", hash = "sha256:12a62691eb5aac58d65200c7ae94d73e8a65c331c3a86a2e9670927e94339ee8"}, - {file = "aiohttp-3.11.18-cp312-cp312-win_amd64.whl", hash = "sha256:364329f319c499128fd5cd2d1c31c44f234c58f9b96cc57f743d16ec4f3238c8"}, - {file = "aiohttp-3.11.18-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:474215ec618974054cf5dc465497ae9708543cbfc312c65212325d4212525811"}, - {file = "aiohttp-3.11.18-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6ced70adf03920d4e67c373fd692123e34d3ac81dfa1c27e45904a628567d804"}, - {file = "aiohttp-3.11.18-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2d9f6c0152f8d71361905aaf9ed979259537981f47ad099c8b3d81e0319814bd"}, - {file = "aiohttp-3.11.18-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a35197013ed929c0aed5c9096de1fc5a9d336914d73ab3f9df14741668c0616c"}, - {file = "aiohttp-3.11.18-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:540b8a1f3a424f1af63e0af2d2853a759242a1769f9f1ab053996a392bd70118"}, - {file = "aiohttp-3.11.18-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9e6710ebebfce2ba21cee6d91e7452d1125100f41b906fb5af3da8c78b764c1"}, - {file = "aiohttp-3.11.18-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8af2ef3b4b652ff109f98087242e2ab974b2b2b496304063585e3d78de0b000"}, - {file = "aiohttp-3.11.18-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:28c3f975e5ae3dbcbe95b7e3dcd30e51da561a0a0f2cfbcdea30fc1308d72137"}, - {file = "aiohttp-3.11.18-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c28875e316c7b4c3e745172d882d8a5c835b11018e33432d281211af35794a93"}, - {file = "aiohttp-3.11.18-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:13cd38515568ae230e1ef6919e2e33da5d0f46862943fcda74e7e915096815f3"}, - {file = "aiohttp-3.11.18-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0e2a92101efb9f4c2942252c69c63ddb26d20f46f540c239ccfa5af865197bb8"}, - {file = "aiohttp-3.11.18-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e6d3e32b8753c8d45ac550b11a1090dd66d110d4ef805ffe60fa61495360b3b2"}, - {file = "aiohttp-3.11.18-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ea4cf2488156e0f281f93cc2fd365025efcba3e2d217cbe3df2840f8c73db261"}, - {file = "aiohttp-3.11.18-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d4df95ad522c53f2b9ebc07f12ccd2cb15550941e11a5bbc5ddca2ca56316d7"}, - {file = "aiohttp-3.11.18-cp313-cp313-win32.whl", hash = "sha256:cdd1bbaf1e61f0d94aced116d6e95fe25942f7a5f42382195fd9501089db5d78"}, - {file = "aiohttp-3.11.18-cp313-cp313-win_amd64.whl", hash = "sha256:bdd619c27e44382cf642223f11cfd4d795161362a5a1fc1fa3940397bc89db01"}, - {file = "aiohttp-3.11.18-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:469ac32375d9a716da49817cd26f1916ec787fc82b151c1c832f58420e6d3533"}, - {file = "aiohttp-3.11.18-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3cec21dd68924179258ae14af9f5418c1ebdbba60b98c667815891293902e5e0"}, - {file = "aiohttp-3.11.18-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b426495fb9140e75719b3ae70a5e8dd3a79def0ae3c6c27e012fc59f16544a4a"}, - {file = "aiohttp-3.11.18-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad2f41203e2808616292db5d7170cccf0c9f9c982d02544443c7eb0296e8b0c7"}, - {file = "aiohttp-3.11.18-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5bc0ae0a5e9939e423e065a3e5b00b24b8379f1db46046d7ab71753dfc7dd0e1"}, - {file = "aiohttp-3.11.18-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe7cdd3f7d1df43200e1c80f1aed86bb36033bf65e3c7cf46a2b97a253ef8798"}, - {file = "aiohttp-3.11.18-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5199be2a2f01ffdfa8c3a6f5981205242986b9e63eb8ae03fd18f736e4840721"}, - {file = "aiohttp-3.11.18-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ccec9e72660b10f8e283e91aa0295975c7bd85c204011d9f5eb69310555cf30"}, - {file = "aiohttp-3.11.18-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1596ebf17e42e293cbacc7a24c3e0dc0f8f755b40aff0402cb74c1ff6baec1d3"}, - {file = "aiohttp-3.11.18-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:eab7b040a8a873020113ba814b7db7fa935235e4cbaf8f3da17671baa1024863"}, - {file = "aiohttp-3.11.18-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:5d61df4a05476ff891cff0030329fee4088d40e4dc9b013fac01bc3c745542c2"}, - {file = "aiohttp-3.11.18-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:46533e6792e1410f9801d09fd40cbbff3f3518d1b501d6c3c5b218f427f6ff08"}, - {file = "aiohttp-3.11.18-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c1b90407ced992331dd6d4f1355819ea1c274cc1ee4d5b7046c6761f9ec11829"}, - {file = "aiohttp-3.11.18-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a2fd04ae4971b914e54fe459dd7edbbd3f2ba875d69e057d5e3c8e8cac094935"}, - {file = "aiohttp-3.11.18-cp39-cp39-win32.whl", hash = "sha256:b2f317d1678002eee6fe85670039fb34a757972284614638f82b903a03feacdc"}, - {file = "aiohttp-3.11.18-cp39-cp39-win_amd64.whl", hash = "sha256:5e7007b8d1d09bce37b54111f593d173691c530b80f27c6493b928dabed9e6ef"}, - {file = "aiohttp-3.11.18.tar.gz", hash = "sha256:ae856e1138612b7e412db63b7708735cff4d38d0399f6a5435d3dac2669f558a"}, + {file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5421af8f22a98f640261ee48aae3a37f0c41371e99412d55eaf2f8a46d5dad29"}, + {file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fcda86f6cb318ba36ed8f1396a6a4a3fd8f856f84d426584392083d10da4de0"}, + {file = "aiohttp-3.12.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cd71c9fb92aceb5a23c4c39d8ecc80389c178eba9feab77f19274843eb9412d"}, + {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34ebf1aca12845066c963016655dac897651e1544f22a34c9b461ac3b4b1d3aa"}, + {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:893a4639694c5b7edd4bdd8141be296042b6806e27cc1d794e585c43010cc294"}, + {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:663d8ee3ffb3494502ebcccb49078faddbb84c1d870f9c1dd5a29e85d1f747ce"}, + {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0f8f6a85a0006ae2709aa4ce05749ba2cdcb4b43d6c21a16c8517c16593aabe"}, + {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1582745eb63df267c92d8b61ca655a0ce62105ef62542c00a74590f306be8cb5"}, + {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d59227776ee2aa64226f7e086638baa645f4b044f2947dbf85c76ab11dcba073"}, + {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06b07c418bde1c8e737d8fa67741072bd3f5b0fb66cf8c0655172188c17e5fa6"}, + {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:9445c1842680efac0f81d272fd8db7163acfcc2b1436e3f420f4c9a9c5a50795"}, + {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:09c4767af0b0b98c724f5d47f2bf33395c8986995b0a9dab0575ca81a554a8c0"}, + {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f3854fbde7a465318ad8d3fc5bef8f059e6d0a87e71a0d3360bb56c0bf87b18a"}, + {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2332b4c361c05ecd381edb99e2a33733f3db906739a83a483974b3df70a51b40"}, + {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1561db63fa1b658cd94325d303933553ea7d89ae09ff21cc3bcd41b8521fbbb6"}, + {file = "aiohttp-3.12.13-cp310-cp310-win32.whl", hash = "sha256:a0be857f0b35177ba09d7c472825d1b711d11c6d0e8a2052804e3b93166de1ad"}, + {file = "aiohttp-3.12.13-cp310-cp310-win_amd64.whl", hash = "sha256:fcc30ad4fb5cb41a33953292d45f54ef4066746d625992aeac33b8c681173178"}, + {file = "aiohttp-3.12.13-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c229b1437aa2576b99384e4be668af1db84b31a45305d02f61f5497cfa6f60c"}, + {file = "aiohttp-3.12.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:04076d8c63471e51e3689c93940775dc3d12d855c0c80d18ac5a1c68f0904358"}, + {file = "aiohttp-3.12.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:55683615813ce3601640cfaa1041174dc956d28ba0511c8cbd75273eb0587014"}, + {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:921bc91e602d7506d37643e77819cb0b840d4ebb5f8d6408423af3d3bf79a7b7"}, + {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e72d17fe0974ddeae8ed86db297e23dba39c7ac36d84acdbb53df2e18505a013"}, + {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0653d15587909a52e024a261943cf1c5bdc69acb71f411b0dd5966d065a51a47"}, + {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a77b48997c66722c65e157c06c74332cdf9c7ad00494b85ec43f324e5c5a9b9a"}, + {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6946bae55fd36cfb8e4092c921075cde029c71c7cb571d72f1079d1e4e013bc"}, + {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f95db8c8b219bcf294a53742c7bda49b80ceb9d577c8e7aa075612b7f39ffb7"}, + {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:03d5eb3cfb4949ab4c74822fb3326cd9655c2b9fe22e4257e2100d44215b2e2b"}, + {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6383dd0ffa15515283c26cbf41ac8e6705aab54b4cbb77bdb8935a713a89bee9"}, + {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6548a411bc8219b45ba2577716493aa63b12803d1e5dc70508c539d0db8dbf5a"}, + {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:81b0fcbfe59a4ca41dc8f635c2a4a71e63f75168cc91026c61be665945739e2d"}, + {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6a83797a0174e7995e5edce9dcecc517c642eb43bc3cba296d4512edf346eee2"}, + {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5734d8469a5633a4e9ffdf9983ff7cdb512524645c7a3d4bc8a3de45b935ac3"}, + {file = "aiohttp-3.12.13-cp311-cp311-win32.whl", hash = "sha256:fef8d50dfa482925bb6b4c208b40d8e9fa54cecba923dc65b825a72eed9a5dbd"}, + {file = "aiohttp-3.12.13-cp311-cp311-win_amd64.whl", hash = "sha256:9a27da9c3b5ed9d04c36ad2df65b38a96a37e9cfba6f1381b842d05d98e6afe9"}, + {file = "aiohttp-3.12.13-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0aa580cf80558557285b49452151b9c69f2fa3ad94c5c9e76e684719a8791b73"}, + {file = "aiohttp-3.12.13-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b103a7e414b57e6939cc4dece8e282cfb22043efd0c7298044f6594cf83ab347"}, + {file = "aiohttp-3.12.13-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78f64e748e9e741d2eccff9597d09fb3cd962210e5b5716047cbb646dc8fe06f"}, + {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c955989bf4c696d2ededc6b0ccb85a73623ae6e112439398935362bacfaaf6"}, + {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d640191016763fab76072c87d8854a19e8e65d7a6fcfcbf017926bdbbb30a7e5"}, + {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4dc507481266b410dede95dd9f26c8d6f5a14315372cc48a6e43eac652237d9b"}, + {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8a94daa873465d518db073bd95d75f14302e0208a08e8c942b2f3f1c07288a75"}, + {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f52420cde4ce0bb9425a375d95577fe082cb5721ecb61da3049b55189e4e6"}, + {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f7df1f620ec40f1a7fbcb99ea17d7326ea6996715e78f71a1c9a021e31b96b8"}, + {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3062d4ad53b36e17796dce1c0d6da0ad27a015c321e663657ba1cc7659cfc710"}, + {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:8605e22d2a86b8e51ffb5253d9045ea73683d92d47c0b1438e11a359bdb94462"}, + {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:54fbbe6beafc2820de71ece2198458a711e224e116efefa01b7969f3e2b3ddae"}, + {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:050bd277dfc3768b606fd4eae79dd58ceda67d8b0b3c565656a89ae34525d15e"}, + {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2637a60910b58f50f22379b6797466c3aa6ae28a6ab6404e09175ce4955b4e6a"}, + {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e986067357550d1aaa21cfe9897fa19e680110551518a5a7cf44e6c5638cb8b5"}, + {file = "aiohttp-3.12.13-cp312-cp312-win32.whl", hash = "sha256:ac941a80aeea2aaae2875c9500861a3ba356f9ff17b9cb2dbfb5cbf91baaf5bf"}, + {file = "aiohttp-3.12.13-cp312-cp312-win_amd64.whl", hash = "sha256:671f41e6146a749b6c81cb7fd07f5a8356d46febdaaaf07b0e774ff04830461e"}, + {file = "aiohttp-3.12.13-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d4a18e61f271127465bdb0e8ff36e8f02ac4a32a80d8927aa52371e93cd87938"}, + {file = "aiohttp-3.12.13-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:532542cb48691179455fab429cdb0d558b5e5290b033b87478f2aa6af5d20ace"}, + {file = "aiohttp-3.12.13-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d7eea18b52f23c050ae9db5d01f3d264ab08f09e7356d6f68e3f3ac2de9dfabb"}, + {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad7c8e5c25f2a26842a7c239de3f7b6bfb92304593ef997c04ac49fb703ff4d7"}, + {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6af355b483e3fe9d7336d84539fef460120c2f6e50e06c658fe2907c69262d6b"}, + {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a95cf9f097498f35c88e3609f55bb47b28a5ef67f6888f4390b3d73e2bac6177"}, + {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8ed8c38a1c584fe99a475a8f60eefc0b682ea413a84c6ce769bb19a7ff1c5ef"}, + {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a0b9170d5d800126b5bc89d3053a2363406d6e327afb6afaeda2d19ee8bb103"}, + {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:372feeace612ef8eb41f05ae014a92121a512bd5067db8f25101dd88a8db11da"}, + {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a946d3702f7965d81f7af7ea8fb03bb33fe53d311df48a46eeca17e9e0beed2d"}, + {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a0c4725fae86555bbb1d4082129e21de7264f4ab14baf735278c974785cd2041"}, + {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9b28ea2f708234f0a5c44eb6c7d9eb63a148ce3252ba0140d050b091b6e842d1"}, + {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d4f5becd2a5791829f79608c6f3dc745388162376f310eb9c142c985f9441cc1"}, + {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:60f2ce6b944e97649051d5f5cc0f439360690b73909230e107fd45a359d3e911"}, + {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:69fc1909857401b67bf599c793f2183fbc4804717388b0b888f27f9929aa41f3"}, + {file = "aiohttp-3.12.13-cp313-cp313-win32.whl", hash = "sha256:7d7e68787a2046b0e44ba5587aa723ce05d711e3a3665b6b7545328ac8e3c0dd"}, + {file = "aiohttp-3.12.13-cp313-cp313-win_amd64.whl", hash = "sha256:5a178390ca90419bfd41419a809688c368e63c86bd725e1186dd97f6b89c2706"}, + {file = "aiohttp-3.12.13-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:36f6c973e003dc9b0bb4e8492a643641ea8ef0e97ff7aaa5c0f53d68839357b4"}, + {file = "aiohttp-3.12.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6cbfc73179bd67c229eb171e2e3745d2afd5c711ccd1e40a68b90427f282eab1"}, + {file = "aiohttp-3.12.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1e8b27b2d414f7e3205aa23bb4a692e935ef877e3a71f40d1884f6e04fd7fa74"}, + {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eabded0c2b2ef56243289112c48556c395d70150ce4220d9008e6b4b3dd15690"}, + {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:003038e83f1a3ff97409999995ec02fe3008a1d675478949643281141f54751d"}, + {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b6f46613031dbc92bdcaad9c4c22c7209236ec501f9c0c5f5f0b6a689bf50f3"}, + {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c332c6bb04650d59fb94ed96491f43812549a3ba6e7a16a218e612f99f04145e"}, + {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fea41a2c931fb582cb15dc86a3037329e7b941df52b487a9f8b5aa960153cbd"}, + {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:846104f45d18fb390efd9b422b27d8f3cf8853f1218c537f36e71a385758c896"}, + {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d6c85ac7dd350f8da2520bac8205ce99df4435b399fa7f4dc4a70407073e390"}, + {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5a1ecce0ed281bec7da8550da052a6b89552db14d0a0a45554156f085a912f48"}, + {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:5304d74867028cca8f64f1cc1215eb365388033c5a691ea7aa6b0dc47412f495"}, + {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:64d1f24ee95a2d1e094a4cd7a9b7d34d08db1bbcb8aa9fb717046b0a884ac294"}, + {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:119c79922a7001ca6a9e253228eb39b793ea994fd2eccb79481c64b5f9d2a055"}, + {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bb18f00396d22e2f10cd8825d671d9f9a3ba968d708a559c02a627536b36d91c"}, + {file = "aiohttp-3.12.13-cp39-cp39-win32.whl", hash = "sha256:0022de47ef63fd06b065d430ac79c6b0bd24cdae7feaf0e8c6bac23b805a23a8"}, + {file = "aiohttp-3.12.13-cp39-cp39-win_amd64.whl", hash = "sha256:29e08111ccf81b2734ae03f1ad1cb03b9615e7d8f616764f22f71209c094f122"}, + {file = "aiohttp-3.12.13.tar.gz", hash = "sha256:47e2da578528264a12e4e3dd8dd72a7289e5f812758fe086473fab037a10fcce"}, ] [package.dependencies] -aiohappyeyeballs = ">=2.3.0" +aiohappyeyeballs = ">=2.5.0" aiosignal = ">=1.1.2" async-timeout = {version = ">=4.0,<6.0", markers = "python_version < \"3.11\""} attrs = ">=17.3.0" @@ -114,7 +119,7 @@ propcache = ">=0.2.0" yarl = ">=1.17.0,<2.0" [package.extras] -speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.2.0) ; sys_platform == \"linux\" or sys_platform == \"darwin\"", "brotlicffi ; platform_python_implementation != \"CPython\""] +speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.3.0)", "brotlicffi ; platform_python_implementation != \"CPython\""] [[package]] name = "aiosignal" From b33bca08f453b3c910d0b4cc624d09f22bfe8fcb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 18:42:16 +0200 Subject: [PATCH 199/264] Bump requests from 2.32.3 to 2.32.4 (#241) Bumps [requests](https://github.com/psf/requests) from 2.32.3 to 2.32.4. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.32.3...v2.32.4) --- updated-dependencies: - dependency-name: requests dependency-version: 2.32.4 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 1314725..5911a4c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1462,19 +1462,19 @@ md = ["cmarkgfm (>=0.8.0)"] [[package]] name = "requests" -version = "2.32.3" +version = "2.32.4" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" groups = ["main", "dev"] files = [ - {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, - {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, + {file = "requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c"}, + {file = "requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422"}, ] [package.dependencies] certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" +charset_normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<3" From 9f60efb71d8576aeede0a0622439cc577ce26bf4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 18:45:17 +0200 Subject: [PATCH 200/264] Bump urllib3 from 2.4.0 to 2.5.0 (#243) Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.4.0 to 2.5.0. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/2.4.0...2.5.0) --- updated-dependencies: - dependency-name: urllib3 dependency-version: 2.5.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 5911a4c..1ea5604 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1634,14 +1634,14 @@ files = [ [[package]] name = "urllib3" -version = "2.4.0" +version = "2.5.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" groups = ["main", "dev"] files = [ - {file = "urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813"}, - {file = "urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466"}, + {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, + {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, ] [package.extras] From 44212b9d7d03a0d15c0f850e43a3ad26b2e4fff1 Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Sun, 18 May 2025 13:34:02 +0200 Subject: [PATCH 201/264] Upgrade version --- README.rst | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index f8f1474..164cee2 100644 --- a/README.rst +++ b/README.rst @@ -38,7 +38,7 @@ To build the library run : .. code-block:: bash - $ python setup.py install + $ poetry build Run the client diff --git a/pyproject.toml b/pyproject.toml index c51794e..ff84479 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ exclude = ''' [tool.poetry] name = "cloudfoundry_client" -version = "1.38.2" +version = "1.38.3" description = "A client library for CloudFoundry" authors = ["Benjamin Einaudi "] readme = "README.rst" From 8f70b6f0554213f5ba2b8c291e988cf073381e4f Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Fri, 20 Jun 2025 19:05:45 +0200 Subject: [PATCH 202/264] Prepare version 1.38.4 --- cloudfoundry_client/__init__.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudfoundry_client/__init__.py b/cloudfoundry_client/__init__.py index 6d29d24..d9ce17c 100644 --- a/cloudfoundry_client/__init__.py +++ b/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.38.2" +__version__ = "1.38.4" diff --git a/pyproject.toml b/pyproject.toml index ff84479..f82eb7a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ exclude = ''' [tool.poetry] name = "cloudfoundry_client" -version = "1.38.3" +version = "1.38.4" description = "A client library for CloudFoundry" authors = ["Benjamin Einaudi "] readme = "README.rst" From 31b373d3a8d3edfb88577ac8c26350c6d8e02f9d Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Fri, 20 Jun 2025 19:09:37 +0200 Subject: [PATCH 203/264] chore(poetry) upgrade lock file --- poetry.lock | 866 ++++++++++++++++++++++++++-------------------------- 1 file changed, 436 insertions(+), 430 deletions(-) diff --git a/poetry.lock b/poetry.lock index 1ea5604..ebf4f62 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -235,14 +235,14 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" -version = "2025.4.26" +version = "2025.6.15" description = "Python package for providing Mozilla's CA Bundle." optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" groups = ["main", "dev"] files = [ - {file = "certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3"}, - {file = "certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6"}, + {file = "certifi-2025.6.15-py3-none-any.whl", hash = "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057"}, + {file = "certifi-2025.6.15.tar.gz", hash = "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b"}, ] [[package]] @@ -557,116 +557,116 @@ pyflakes = ">=3.3.0,<3.4.0" [[package]] name = "frozenlist" -version = "1.6.0" +version = "1.7.0" description = "A list-like structure which implements collections.abc.MutableSequence" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "frozenlist-1.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e6e558ea1e47fd6fa8ac9ccdad403e5dd5ecc6ed8dda94343056fa4277d5c65e"}, - {file = "frozenlist-1.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f4b3cd7334a4bbc0c472164f3744562cb72d05002cc6fcf58adb104630bbc352"}, - {file = "frozenlist-1.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9799257237d0479736e2b4c01ff26b5c7f7694ac9692a426cb717f3dc02fff9b"}, - {file = "frozenlist-1.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3a7bb0fe1f7a70fb5c6f497dc32619db7d2cdd53164af30ade2f34673f8b1fc"}, - {file = "frozenlist-1.6.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:36d2fc099229f1e4237f563b2a3e0ff7ccebc3999f729067ce4e64a97a7f2869"}, - {file = "frozenlist-1.6.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f27a9f9a86dcf00708be82359db8de86b80d029814e6693259befe82bb58a106"}, - {file = "frozenlist-1.6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75ecee69073312951244f11b8627e3700ec2bfe07ed24e3a685a5979f0412d24"}, - {file = "frozenlist-1.6.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2c7d5aa19714b1b01a0f515d078a629e445e667b9da869a3cd0e6fe7dec78bd"}, - {file = "frozenlist-1.6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69bbd454f0fb23b51cadc9bdba616c9678e4114b6f9fa372d462ff2ed9323ec8"}, - {file = "frozenlist-1.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7daa508e75613809c7a57136dec4871a21bca3080b3a8fc347c50b187df4f00c"}, - {file = "frozenlist-1.6.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:89ffdb799154fd4d7b85c56d5fa9d9ad48946619e0eb95755723fffa11022d75"}, - {file = "frozenlist-1.6.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:920b6bd77d209931e4c263223381d63f76828bec574440f29eb497cf3394c249"}, - {file = "frozenlist-1.6.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d3ceb265249fb401702fce3792e6b44c1166b9319737d21495d3611028d95769"}, - {file = "frozenlist-1.6.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:52021b528f1571f98a7d4258c58aa8d4b1a96d4f01d00d51f1089f2e0323cb02"}, - {file = "frozenlist-1.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0f2ca7810b809ed0f1917293050163c7654cefc57a49f337d5cd9de717b8fad3"}, - {file = "frozenlist-1.6.0-cp310-cp310-win32.whl", hash = "sha256:0e6f8653acb82e15e5443dba415fb62a8732b68fe09936bb6d388c725b57f812"}, - {file = "frozenlist-1.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:f1a39819a5a3e84304cd286e3dc62a549fe60985415851b3337b6f5cc91907f1"}, - {file = "frozenlist-1.6.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ae8337990e7a45683548ffb2fee1af2f1ed08169284cd829cdd9a7fa7470530d"}, - {file = "frozenlist-1.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8c952f69dd524558694818a461855f35d36cc7f5c0adddce37e962c85d06eac0"}, - {file = "frozenlist-1.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8f5fef13136c4e2dee91bfb9a44e236fff78fc2cd9f838eddfc470c3d7d90afe"}, - {file = "frozenlist-1.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:716bbba09611b4663ecbb7cd022f640759af8259e12a6ca939c0a6acd49eedba"}, - {file = "frozenlist-1.6.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7b8c4dc422c1a3ffc550b465090e53b0bf4839047f3e436a34172ac67c45d595"}, - {file = "frozenlist-1.6.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b11534872256e1666116f6587a1592ef395a98b54476addb5e8d352925cb5d4a"}, - {file = "frozenlist-1.6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c6eceb88aaf7221f75be6ab498dc622a151f5f88d536661af3ffc486245a626"}, - {file = "frozenlist-1.6.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62c828a5b195570eb4b37369fcbbd58e96c905768d53a44d13044355647838ff"}, - {file = "frozenlist-1.6.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1c6bd2c6399920c9622362ce95a7d74e7f9af9bfec05fff91b8ce4b9647845a"}, - {file = "frozenlist-1.6.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:49ba23817781e22fcbd45fd9ff2b9b8cdb7b16a42a4851ab8025cae7b22e96d0"}, - {file = "frozenlist-1.6.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:431ef6937ae0f853143e2ca67d6da76c083e8b1fe3df0e96f3802fd37626e606"}, - {file = "frozenlist-1.6.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9d124b38b3c299ca68433597ee26b7819209cb8a3a9ea761dfe9db3a04bba584"}, - {file = "frozenlist-1.6.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:118e97556306402e2b010da1ef21ea70cb6d6122e580da64c056b96f524fbd6a"}, - {file = "frozenlist-1.6.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fb3b309f1d4086b5533cf7bbcf3f956f0ae6469664522f1bde4feed26fba60f1"}, - {file = "frozenlist-1.6.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54dece0d21dce4fdb188a1ffc555926adf1d1c516e493c2914d7c370e454bc9e"}, - {file = "frozenlist-1.6.0-cp311-cp311-win32.whl", hash = "sha256:654e4ba1d0b2154ca2f096bed27461cf6160bc7f504a7f9a9ef447c293caf860"}, - {file = "frozenlist-1.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:3e911391bffdb806001002c1f860787542f45916c3baf764264a52765d5a5603"}, - {file = "frozenlist-1.6.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c5b9e42ace7d95bf41e19b87cec8f262c41d3510d8ad7514ab3862ea2197bfb1"}, - {file = "frozenlist-1.6.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ca9973735ce9f770d24d5484dcb42f68f135351c2fc81a7a9369e48cf2998a29"}, - {file = "frozenlist-1.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6ac40ec76041c67b928ca8aaffba15c2b2ee3f5ae8d0cb0617b5e63ec119ca25"}, - {file = "frozenlist-1.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95b7a8a3180dfb280eb044fdec562f9b461614c0ef21669aea6f1d3dac6ee576"}, - {file = "frozenlist-1.6.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c444d824e22da6c9291886d80c7d00c444981a72686e2b59d38b285617cb52c8"}, - {file = "frozenlist-1.6.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb52c8166499a8150bfd38478248572c924c003cbb45fe3bcd348e5ac7c000f9"}, - {file = "frozenlist-1.6.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b35298b2db9c2468106278537ee529719228950a5fdda686582f68f247d1dc6e"}, - {file = "frozenlist-1.6.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d108e2d070034f9d57210f22fefd22ea0d04609fc97c5f7f5a686b3471028590"}, - {file = "frozenlist-1.6.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e1be9111cb6756868ac242b3c2bd1f09d9aea09846e4f5c23715e7afb647103"}, - {file = "frozenlist-1.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:94bb451c664415f02f07eef4ece976a2c65dcbab9c2f1705b7031a3a75349d8c"}, - {file = "frozenlist-1.6.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:d1a686d0b0949182b8faddea596f3fc11f44768d1f74d4cad70213b2e139d821"}, - {file = "frozenlist-1.6.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ea8e59105d802c5a38bdbe7362822c522230b3faba2aa35c0fa1765239b7dd70"}, - {file = "frozenlist-1.6.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:abc4e880a9b920bc5020bf6a431a6bb40589d9bca3975c980495f63632e8382f"}, - {file = "frozenlist-1.6.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9a79713adfe28830f27a3c62f6b5406c37376c892b05ae070906f07ae4487046"}, - {file = "frozenlist-1.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a0318c2068e217a8f5e3b85e35899f5a19e97141a45bb925bb357cfe1daf770"}, - {file = "frozenlist-1.6.0-cp312-cp312-win32.whl", hash = "sha256:853ac025092a24bb3bf09ae87f9127de9fe6e0c345614ac92536577cf956dfcc"}, - {file = "frozenlist-1.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:2bdfe2d7e6c9281c6e55523acd6c2bf77963cb422fdc7d142fb0cb6621b66878"}, - {file = "frozenlist-1.6.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1d7fb014fe0fbfee3efd6a94fc635aeaa68e5e1720fe9e57357f2e2c6e1a647e"}, - {file = "frozenlist-1.6.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01bcaa305a0fdad12745502bfd16a1c75b14558dabae226852f9159364573117"}, - {file = "frozenlist-1.6.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8b314faa3051a6d45da196a2c495e922f987dc848e967d8cfeaee8a0328b1cd4"}, - {file = "frozenlist-1.6.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da62fecac21a3ee10463d153549d8db87549a5e77eefb8c91ac84bb42bb1e4e3"}, - {file = "frozenlist-1.6.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1eb89bf3454e2132e046f9599fbcf0a4483ed43b40f545551a39316d0201cd1"}, - {file = "frozenlist-1.6.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18689b40cb3936acd971f663ccb8e2589c45db5e2c5f07e0ec6207664029a9c"}, - {file = "frozenlist-1.6.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e67ddb0749ed066b1a03fba812e2dcae791dd50e5da03be50b6a14d0c1a9ee45"}, - {file = "frozenlist-1.6.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc5e64626e6682638d6e44398c9baf1d6ce6bc236d40b4b57255c9d3f9761f1f"}, - {file = "frozenlist-1.6.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:437cfd39564744ae32ad5929e55b18ebd88817f9180e4cc05e7d53b75f79ce85"}, - {file = "frozenlist-1.6.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:62dd7df78e74d924952e2feb7357d826af8d2f307557a779d14ddf94d7311be8"}, - {file = "frozenlist-1.6.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a66781d7e4cddcbbcfd64de3d41a61d6bdde370fc2e38623f30b2bd539e84a9f"}, - {file = "frozenlist-1.6.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:482fe06e9a3fffbcd41950f9d890034b4a54395c60b5e61fae875d37a699813f"}, - {file = "frozenlist-1.6.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e4f9373c500dfc02feea39f7a56e4f543e670212102cc2eeb51d3a99c7ffbde6"}, - {file = "frozenlist-1.6.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e69bb81de06827147b7bfbaeb284d85219fa92d9f097e32cc73675f279d70188"}, - {file = "frozenlist-1.6.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7613d9977d2ab4a9141dde4a149f4357e4065949674c5649f920fec86ecb393e"}, - {file = "frozenlist-1.6.0-cp313-cp313-win32.whl", hash = "sha256:4def87ef6d90429f777c9d9de3961679abf938cb6b7b63d4a7eb8a268babfce4"}, - {file = "frozenlist-1.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:37a8a52c3dfff01515e9bbbee0e6063181362f9de3db2ccf9bc96189b557cbfd"}, - {file = "frozenlist-1.6.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:46138f5a0773d064ff663d273b309b696293d7a7c00a0994c5c13a5078134b64"}, - {file = "frozenlist-1.6.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:f88bc0a2b9c2a835cb888b32246c27cdab5740059fb3688852bf91e915399b91"}, - {file = "frozenlist-1.6.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:777704c1d7655b802c7850255639672e90e81ad6fa42b99ce5ed3fbf45e338dd"}, - {file = "frozenlist-1.6.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85ef8d41764c7de0dcdaf64f733a27352248493a85a80661f3c678acd27e31f2"}, - {file = "frozenlist-1.6.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:da5cb36623f2b846fb25009d9d9215322318ff1c63403075f812b3b2876c8506"}, - {file = "frozenlist-1.6.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cbb56587a16cf0fb8acd19e90ff9924979ac1431baea8681712716a8337577b0"}, - {file = "frozenlist-1.6.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6154c3ba59cda3f954c6333025369e42c3acd0c6e8b6ce31eb5c5b8116c07e0"}, - {file = "frozenlist-1.6.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e8246877afa3f1ae5c979fe85f567d220f86a50dc6c493b9b7d8191181ae01e"}, - {file = "frozenlist-1.6.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b0f6cce16306d2e117cf9db71ab3a9e8878a28176aeaf0dbe35248d97b28d0c"}, - {file = "frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1b8e8cd8032ba266f91136d7105706ad57770f3522eac4a111d77ac126a25a9b"}, - {file = "frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:e2ada1d8515d3ea5378c018a5f6d14b4994d4036591a52ceaf1a1549dec8e1ad"}, - {file = "frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:cdb2c7f071e4026c19a3e32b93a09e59b12000751fc9b0b7758da899e657d215"}, - {file = "frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:03572933a1969a6d6ab509d509e5af82ef80d4a5d4e1e9f2e1cdd22c77a3f4d2"}, - {file = "frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:77effc978947548b676c54bbd6a08992759ea6f410d4987d69feea9cd0919911"}, - {file = "frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a2bda8be77660ad4089caf2223fdbd6db1858462c4b85b67fbfa22102021e497"}, - {file = "frozenlist-1.6.0-cp313-cp313t-win32.whl", hash = "sha256:a4d96dc5bcdbd834ec6b0f91027817214216b5b30316494d2b1aebffb87c534f"}, - {file = "frozenlist-1.6.0-cp313-cp313t-win_amd64.whl", hash = "sha256:e18036cb4caa17ea151fd5f3d70be9d354c99eb8cf817a3ccde8a7873b074348"}, - {file = "frozenlist-1.6.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:536a1236065c29980c15c7229fbb830dedf809708c10e159b8136534233545f0"}, - {file = "frozenlist-1.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ed5e3a4462ff25ca84fb09e0fada8ea267df98a450340ead4c91b44857267d70"}, - {file = "frozenlist-1.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e19c0fc9f4f030fcae43b4cdec9e8ab83ffe30ec10c79a4a43a04d1af6c5e1ad"}, - {file = "frozenlist-1.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c608f833897501dac548585312d73a7dca028bf3b8688f0d712b7acfaf7fb3"}, - {file = "frozenlist-1.6.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0dbae96c225d584f834b8d3cc688825911960f003a85cb0fd20b6e5512468c42"}, - {file = "frozenlist-1.6.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:625170a91dd7261a1d1c2a0c1a353c9e55d21cd67d0852185a5fef86587e6f5f"}, - {file = "frozenlist-1.6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1db8b2fc7ee8a940b547a14c10e56560ad3ea6499dc6875c354e2335812f739d"}, - {file = "frozenlist-1.6.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4da6fc43048b648275a220e3a61c33b7fff65d11bdd6dcb9d9c145ff708b804c"}, - {file = "frozenlist-1.6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef8e7e8f2f3820c5f175d70fdd199b79e417acf6c72c5d0aa8f63c9f721646f"}, - {file = "frozenlist-1.6.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:aa733d123cc78245e9bb15f29b44ed9e5780dc6867cfc4e544717b91f980af3b"}, - {file = "frozenlist-1.6.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:ba7f8d97152b61f22d7f59491a781ba9b177dd9f318486c5fbc52cde2db12189"}, - {file = "frozenlist-1.6.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:56a0b8dd6d0d3d971c91f1df75e824986667ccce91e20dca2023683814344791"}, - {file = "frozenlist-1.6.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:5c9e89bf19ca148efcc9e3c44fd4c09d5af85c8a7dd3dbd0da1cb83425ef4983"}, - {file = "frozenlist-1.6.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:1330f0a4376587face7637dfd245380a57fe21ae8f9d360c1c2ef8746c4195fa"}, - {file = "frozenlist-1.6.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2187248203b59625566cac53572ec8c2647a140ee2738b4e36772930377a533c"}, - {file = "frozenlist-1.6.0-cp39-cp39-win32.whl", hash = "sha256:2b8cf4cfea847d6c12af06091561a89740f1f67f331c3fa8623391905e878530"}, - {file = "frozenlist-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:1255d5d64328c5a0d066ecb0f02034d086537925f1f04b50b1ae60d37afbf572"}, - {file = "frozenlist-1.6.0-py3-none-any.whl", hash = "sha256:535eec9987adb04701266b92745d6cdcef2e77669299359c3009c3404dd5d191"}, - {file = "frozenlist-1.6.0.tar.gz", hash = "sha256:b99655c32c1c8e06d111e7f41c06c29a5318cb1835df23a45518e02a47c63b68"}, + {file = "frozenlist-1.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cc4df77d638aa2ed703b878dd093725b72a824c3c546c076e8fdf276f78ee84a"}, + {file = "frozenlist-1.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:716a9973a2cc963160394f701964fe25012600f3d311f60c790400b00e568b61"}, + {file = "frozenlist-1.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0fd1bad056a3600047fb9462cff4c5322cebc59ebf5d0a3725e0ee78955001d"}, + {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3789ebc19cb811163e70fe2bd354cea097254ce6e707ae42e56f45e31e96cb8e"}, + {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af369aa35ee34f132fcfad5be45fbfcde0e3a5f6a1ec0712857f286b7d20cca9"}, + {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac64b6478722eeb7a3313d494f8342ef3478dff539d17002f849101b212ef97c"}, + {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f89f65d85774f1797239693cef07ad4c97fdd0639544bad9ac4b869782eb1981"}, + {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1073557c941395fdfcfac13eb2456cb8aad89f9de27bae29fabca8e563b12615"}, + {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed8d2fa095aae4bdc7fdd80351009a48d286635edffee66bf865e37a9125c50"}, + {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:24c34bea555fe42d9f928ba0a740c553088500377448febecaa82cc3e88aa1fa"}, + {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:69cac419ac6a6baad202c85aaf467b65ac860ac2e7f2ac1686dc40dbb52f6577"}, + {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:960d67d0611f4c87da7e2ae2eacf7ea81a5be967861e0c63cf205215afbfac59"}, + {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:41be2964bd4b15bf575e5daee5a5ce7ed3115320fb3c2b71fca05582ffa4dc9e"}, + {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:46d84d49e00c9429238a7ce02dc0be8f6d7cd0cd405abd1bebdc991bf27c15bd"}, + {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:15900082e886edb37480335d9d518cec978afc69ccbc30bd18610b7c1b22a718"}, + {file = "frozenlist-1.7.0-cp310-cp310-win32.whl", hash = "sha256:400ddd24ab4e55014bba442d917203c73b2846391dd42ca5e38ff52bb18c3c5e"}, + {file = "frozenlist-1.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:6eb93efb8101ef39d32d50bce242c84bcbddb4f7e9febfa7b524532a239b4464"}, + {file = "frozenlist-1.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aa51e147a66b2d74de1e6e2cf5921890de6b0f4820b257465101d7f37b49fb5a"}, + {file = "frozenlist-1.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9b35db7ce1cd71d36ba24f80f0c9e7cff73a28d7a74e91fe83e23d27c7828750"}, + {file = "frozenlist-1.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34a69a85e34ff37791e94542065c8416c1afbf820b68f720452f636d5fb990cd"}, + {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a646531fa8d82c87fe4bb2e596f23173caec9185bfbca5d583b4ccfb95183e2"}, + {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:79b2ffbba483f4ed36a0f236ccb85fbb16e670c9238313709638167670ba235f"}, + {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a26f205c9ca5829cbf82bb2a84b5c36f7184c4316617d7ef1b271a56720d6b30"}, + {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bcacfad3185a623fa11ea0e0634aac7b691aa925d50a440f39b458e41c561d98"}, + {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72c1b0fe8fe451b34f12dce46445ddf14bd2a5bcad7e324987194dc8e3a74c86"}, + {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61d1a5baeaac6c0798ff6edfaeaa00e0e412d49946c53fae8d4b8e8b3566c4ae"}, + {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7edf5c043c062462f09b6820de9854bf28cc6cc5b6714b383149745e287181a8"}, + {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d50ac7627b3a1bd2dcef6f9da89a772694ec04d9a61b66cf87f7d9446b4a0c31"}, + {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce48b2fece5aeb45265bb7a58259f45027db0abff478e3077e12b05b17fb9da7"}, + {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:fe2365ae915a1fafd982c146754e1de6ab3478def8a59c86e1f7242d794f97d5"}, + {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:45a6f2fdbd10e074e8814eb98b05292f27bad7d1883afbe009d96abdcf3bc898"}, + {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:21884e23cffabb157a9dd7e353779077bf5b8f9a58e9b262c6caad2ef5f80a56"}, + {file = "frozenlist-1.7.0-cp311-cp311-win32.whl", hash = "sha256:284d233a8953d7b24f9159b8a3496fc1ddc00f4db99c324bd5fb5f22d8698ea7"}, + {file = "frozenlist-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:387cbfdcde2f2353f19c2f66bbb52406d06ed77519ac7ee21be0232147c2592d"}, + {file = "frozenlist-1.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3dbf9952c4bb0e90e98aec1bd992b3318685005702656bc6f67c1a32b76787f2"}, + {file = "frozenlist-1.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1f5906d3359300b8a9bb194239491122e6cf1444c2efb88865426f170c262cdb"}, + {file = "frozenlist-1.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3dabd5a8f84573c8d10d8859a50ea2dec01eea372031929871368c09fa103478"}, + {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa57daa5917f1738064f302bf2626281a1cb01920c32f711fbc7bc36111058a8"}, + {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c193dda2b6d49f4c4398962810fa7d7c78f032bf45572b3e04dd5249dff27e08"}, + {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe2b675cf0aaa6d61bf8fbffd3c274b3c9b7b1623beb3809df8a81399a4a9c4"}, + {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8fc5d5cda37f62b262405cf9652cf0856839c4be8ee41be0afe8858f17f4c94b"}, + {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d5ce521d1dd7d620198829b87ea002956e4319002ef0bc8d3e6d045cb4646e"}, + {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:488d0a7d6a0008ca0db273c542098a0fa9e7dfaa7e57f70acef43f32b3f69dca"}, + {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:15a7eaba63983d22c54d255b854e8108e7e5f3e89f647fc854bd77a237e767df"}, + {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1eaa7e9c6d15df825bf255649e05bd8a74b04a4d2baa1ae46d9c2d00b2ca2cb5"}, + {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4389e06714cfa9d47ab87f784a7c5be91d3934cd6e9a7b85beef808297cc025"}, + {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:73bd45e1488c40b63fe5a7df892baf9e2a4d4bb6409a2b3b78ac1c6236178e01"}, + {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99886d98e1643269760e5fe0df31e5ae7050788dd288947f7f007209b8c33f08"}, + {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:290a172aae5a4c278c6da8a96222e6337744cd9c77313efe33d5670b9f65fc43"}, + {file = "frozenlist-1.7.0-cp312-cp312-win32.whl", hash = "sha256:426c7bc70e07cfebc178bc4c2bf2d861d720c4fff172181eeb4a4c41d4ca2ad3"}, + {file = "frozenlist-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:563b72efe5da92e02eb68c59cb37205457c977aa7a449ed1b37e6939e5c47c6a"}, + {file = "frozenlist-1.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee80eeda5e2a4e660651370ebffd1286542b67e268aa1ac8d6dbe973120ef7ee"}, + {file = "frozenlist-1.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d1a81c85417b914139e3a9b995d4a1c84559afc839a93cf2cb7f15e6e5f6ed2d"}, + {file = "frozenlist-1.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cbb65198a9132ebc334f237d7b0df163e4de83fb4f2bdfe46c1e654bdb0c5d43"}, + {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dab46c723eeb2c255a64f9dc05b8dd601fde66d6b19cdb82b2e09cc6ff8d8b5d"}, + {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6aeac207a759d0dedd2e40745575ae32ab30926ff4fa49b1635def65806fddee"}, + {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bd8c4e58ad14b4fa7802b8be49d47993182fdd4023393899632c88fd8cd994eb"}, + {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04fb24d104f425da3540ed83cbfc31388a586a7696142004c577fa61c6298c3f"}, + {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a5c505156368e4ea6b53b5ac23c92d7edc864537ff911d2fb24c140bb175e60"}, + {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bd7eb96a675f18aa5c553eb7ddc24a43c8c18f22e1f9925528128c052cdbe00"}, + {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:05579bf020096fe05a764f1f84cd104a12f78eaab68842d036772dc6d4870b4b"}, + {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:376b6222d114e97eeec13d46c486facd41d4f43bab626b7c3f6a8b4e81a5192c"}, + {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0aa7e176ebe115379b5b1c95b4096fb1c17cce0847402e227e712c27bdb5a949"}, + {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3fbba20e662b9c2130dc771e332a99eff5da078b2b2648153a40669a6d0e36ca"}, + {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f3f4410a0a601d349dd406b5713fec59b4cee7e71678d5b17edda7f4655a940b"}, + {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2cdfaaec6a2f9327bf43c933c0319a7c429058e8537c508964a133dffee412e"}, + {file = "frozenlist-1.7.0-cp313-cp313-win32.whl", hash = "sha256:5fc4df05a6591c7768459caba1b342d9ec23fa16195e744939ba5914596ae3e1"}, + {file = "frozenlist-1.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:52109052b9791a3e6b5d1b65f4b909703984b770694d3eb64fad124c835d7cba"}, + {file = "frozenlist-1.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a6f86e4193bb0e235ef6ce3dde5cbabed887e0b11f516ce8a0f4d3b33078ec2d"}, + {file = "frozenlist-1.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:82d664628865abeb32d90ae497fb93df398a69bb3434463d172b80fc25b0dd7d"}, + {file = "frozenlist-1.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:912a7e8375a1c9a68325a902f3953191b7b292aa3c3fb0d71a216221deca460b"}, + {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9537c2777167488d539bc5de2ad262efc44388230e5118868e172dd4a552b146"}, + {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f34560fb1b4c3e30ba35fa9a13894ba39e5acfc5f60f57d8accde65f46cc5e74"}, + {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:acd03d224b0175f5a850edc104ac19040d35419eddad04e7cf2d5986d98427f1"}, + {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2038310bc582f3d6a09b3816ab01737d60bf7b1ec70f5356b09e84fb7408ab1"}, + {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c05e4c8e5f36e5e088caa1bf78a687528f83c043706640a92cb76cd6999384"}, + {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:765bb588c86e47d0b68f23c1bee323d4b703218037765dcf3f25c838c6fecceb"}, + {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:32dc2e08c67d86d0969714dd484fd60ff08ff81d1a1e40a77dd34a387e6ebc0c"}, + {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:c0303e597eb5a5321b4de9c68e9845ac8f290d2ab3f3e2c864437d3c5a30cd65"}, + {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:a47f2abb4e29b3a8d0b530f7c3598badc6b134562b1a5caee867f7c62fee51e3"}, + {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:3d688126c242a6fabbd92e02633414d40f50bb6002fa4cf995a1d18051525657"}, + {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:4e7e9652b3d367c7bd449a727dc79d5043f48b88d0cbfd4f9f1060cf2b414104"}, + {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1a85e345b4c43db8b842cab1feb41be5cc0b10a1830e6295b69d7310f99becaf"}, + {file = "frozenlist-1.7.0-cp313-cp313t-win32.whl", hash = "sha256:3a14027124ddb70dfcee5148979998066897e79f89f64b13328595c4bdf77c81"}, + {file = "frozenlist-1.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3bf8010d71d4507775f658e9823210b7427be36625b387221642725b515dcf3e"}, + {file = "frozenlist-1.7.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cea3dbd15aea1341ea2de490574a4a37ca080b2ae24e4b4f4b51b9057b4c3630"}, + {file = "frozenlist-1.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7d536ee086b23fecc36c2073c371572374ff50ef4db515e4e503925361c24f71"}, + {file = "frozenlist-1.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dfcebf56f703cb2e346315431699f00db126d158455e513bd14089d992101e44"}, + {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:974c5336e61d6e7eb1ea5b929cb645e882aadab0095c5a6974a111e6479f8878"}, + {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c70db4a0ab5ab20878432c40563573229a7ed9241506181bba12f6b7d0dc41cb"}, + {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1137b78384eebaf70560a36b7b229f752fb64d463d38d1304939984d5cb887b6"}, + {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e793a9f01b3e8b5c0bc646fb59140ce0efcc580d22a3468d70766091beb81b35"}, + {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74739ba8e4e38221d2c5c03d90a7e542cb8ad681915f4ca8f68d04f810ee0a87"}, + {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e63344c4e929b1a01e29bc184bbb5fd82954869033765bfe8d65d09e336a677"}, + {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2ea2a7369eb76de2217a842f22087913cdf75f63cf1307b9024ab82dfb525938"}, + {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:836b42f472a0e006e02499cef9352ce8097f33df43baaba3e0a28a964c26c7d2"}, + {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e22b9a99741294b2571667c07d9f8cceec07cb92aae5ccda39ea1b6052ed4319"}, + {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:9a19e85cc503d958abe5218953df722748d87172f71b73cf3c9257a91b999890"}, + {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f22dac33bb3ee8fe3e013aa7b91dc12f60d61d05b7fe32191ffa84c3aafe77bd"}, + {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9ccec739a99e4ccf664ea0775149f2749b8a6418eb5b8384b4dc0a7d15d304cb"}, + {file = "frozenlist-1.7.0-cp39-cp39-win32.whl", hash = "sha256:b3950f11058310008a87757f3eee16a8e1ca97979833239439586857bc25482e"}, + {file = "frozenlist-1.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:43a82fce6769c70f2f5a06248b614a7d268080a9d20f7457ef10ecee5af82b63"}, + {file = "frozenlist-1.7.0-py3-none-any.whl", hash = "sha256:9a5af342e34f7e97caf8c995864c7a396418ae2859cc6fdf1b1073020d516a7e"}, + {file = "frozenlist-1.7.0.tar.gz", hash = "sha256:2e310d81923c2437ea8670467121cc3e9b0f76d3043cc1d2331d56c7fb7a3a8f"}, ] [[package]] @@ -711,7 +711,7 @@ description = "Read metadata from Python packages" optional = false python-versions = ">=3.9" groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version < \"3.10\"" +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version == \"3.9\"" files = [ {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"}, {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"}, @@ -917,116 +917,122 @@ files = [ [[package]] name = "multidict" -version = "6.4.3" +version = "6.5.0" description = "multidict implementation" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "multidict-6.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:32a998bd8a64ca48616eac5a8c1cc4fa38fb244a3facf2eeb14abe186e0f6cc5"}, - {file = "multidict-6.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a54ec568f1fc7f3c313c2f3b16e5db346bf3660e1309746e7fccbbfded856188"}, - {file = "multidict-6.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a7be07e5df178430621c716a63151165684d3e9958f2bbfcb644246162007ab7"}, - {file = "multidict-6.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b128dbf1c939674a50dd0b28f12c244d90e5015e751a4f339a96c54f7275e291"}, - {file = "multidict-6.4.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b9cb19dfd83d35b6ff24a4022376ea6e45a2beba8ef3f0836b8a4b288b6ad685"}, - {file = "multidict-6.4.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3cf62f8e447ea2c1395afa289b332e49e13d07435369b6f4e41f887db65b40bf"}, - {file = "multidict-6.4.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:909f7d43ff8f13d1adccb6a397094adc369d4da794407f8dd592c51cf0eae4b1"}, - {file = "multidict-6.4.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0bb8f8302fbc7122033df959e25777b0b7659b1fd6bcb9cb6bed76b5de67afef"}, - {file = "multidict-6.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:224b79471b4f21169ea25ebc37ed6f058040c578e50ade532e2066562597b8a9"}, - {file = "multidict-6.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a7bd27f7ab3204f16967a6f899b3e8e9eb3362c0ab91f2ee659e0345445e0078"}, - {file = "multidict-6.4.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:99592bd3162e9c664671fd14e578a33bfdba487ea64bcb41d281286d3c870ad7"}, - {file = "multidict-6.4.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a62d78a1c9072949018cdb05d3c533924ef8ac9bcb06cbf96f6d14772c5cd451"}, - {file = "multidict-6.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3ccdde001578347e877ca4f629450973c510e88e8865d5aefbcb89b852ccc666"}, - {file = "multidict-6.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:eccb67b0e78aa2e38a04c5ecc13bab325a43e5159a181a9d1a6723db913cbb3c"}, - {file = "multidict-6.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8b6fcf6054fc4114a27aa865f8840ef3d675f9316e81868e0ad5866184a6cba5"}, - {file = "multidict-6.4.3-cp310-cp310-win32.whl", hash = "sha256:f92c7f62d59373cd93bc9969d2da9b4b21f78283b1379ba012f7ee8127b3152e"}, - {file = "multidict-6.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:b57e28dbc031d13916b946719f213c494a517b442d7b48b29443e79610acd887"}, - {file = "multidict-6.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f6f19170197cc29baccd33ccc5b5d6a331058796485857cf34f7635aa25fb0cd"}, - {file = "multidict-6.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f2882bf27037eb687e49591690e5d491e677272964f9ec7bc2abbe09108bdfb8"}, - {file = "multidict-6.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fbf226ac85f7d6b6b9ba77db4ec0704fde88463dc17717aec78ec3c8546c70ad"}, - {file = "multidict-6.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e329114f82ad4b9dd291bef614ea8971ec119ecd0f54795109976de75c9a852"}, - {file = "multidict-6.4.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1f4e0334d7a555c63f5c8952c57ab6f1c7b4f8c7f3442df689fc9f03df315c08"}, - {file = "multidict-6.4.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:740915eb776617b57142ce0bb13b7596933496e2f798d3d15a20614adf30d229"}, - {file = "multidict-6.4.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255dac25134d2b141c944b59a0d2f7211ca12a6d4779f7586a98b4b03ea80508"}, - {file = "multidict-6.4.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4e8535bd4d741039b5aad4285ecd9b902ef9e224711f0b6afda6e38d7ac02c7"}, - {file = "multidict-6.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c433a33be000dd968f5750722eaa0991037be0be4a9d453eba121774985bc8"}, - {file = "multidict-6.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4eb33b0bdc50acd538f45041f5f19945a1f32b909b76d7b117c0c25d8063df56"}, - {file = "multidict-6.4.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:75482f43465edefd8a5d72724887ccdcd0c83778ded8f0cb1e0594bf71736cc0"}, - {file = "multidict-6.4.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce5b3082e86aee80b3925ab4928198450d8e5b6466e11501fe03ad2191c6d777"}, - {file = "multidict-6.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e413152e3212c4d39f82cf83c6f91be44bec9ddea950ce17af87fbf4e32ca6b2"}, - {file = "multidict-6.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:8aac2eeff69b71f229a405c0a4b61b54bade8e10163bc7b44fcd257949620618"}, - {file = "multidict-6.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ab583ac203af1d09034be41458feeab7863c0635c650a16f15771e1386abf2d7"}, - {file = "multidict-6.4.3-cp311-cp311-win32.whl", hash = "sha256:1b2019317726f41e81154df636a897de1bfe9228c3724a433894e44cd2512378"}, - {file = "multidict-6.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:43173924fa93c7486402217fab99b60baf78d33806af299c56133a3755f69589"}, - {file = "multidict-6.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f1c2f58f08b36f8475f3ec6f5aeb95270921d418bf18f90dffd6be5c7b0e676"}, - {file = "multidict-6.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:26ae9ad364fc61b936fb7bf4c9d8bd53f3a5b4417142cd0be5c509d6f767e2f1"}, - {file = "multidict-6.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:659318c6c8a85f6ecfc06b4e57529e5a78dfdd697260cc81f683492ad7e9435a"}, - {file = "multidict-6.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1eb72c741fd24d5a28242ce72bb61bc91f8451877131fa3fe930edb195f7054"}, - {file = "multidict-6.4.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3cd06d88cb7398252284ee75c8db8e680aa0d321451132d0dba12bc995f0adcc"}, - {file = "multidict-6.4.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4543d8dc6470a82fde92b035a92529317191ce993533c3c0c68f56811164ed07"}, - {file = "multidict-6.4.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:30a3ebdc068c27e9d6081fca0e2c33fdf132ecea703a72ea216b81a66860adde"}, - {file = "multidict-6.4.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b038f10e23f277153f86f95c777ba1958bcd5993194fda26a1d06fae98b2f00c"}, - {file = "multidict-6.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c605a2b2dc14282b580454b9b5d14ebe0668381a3a26d0ac39daa0ca115eb2ae"}, - {file = "multidict-6.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8bd2b875f4ca2bb527fe23e318ddd509b7df163407b0fb717df229041c6df5d3"}, - {file = "multidict-6.4.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c2e98c840c9c8e65c0e04b40c6c5066c8632678cd50c8721fdbcd2e09f21a507"}, - {file = "multidict-6.4.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:66eb80dd0ab36dbd559635e62fba3083a48a252633164857a1d1684f14326427"}, - {file = "multidict-6.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c23831bdee0a2a3cf21be057b5e5326292f60472fb6c6f86392bbf0de70ba731"}, - {file = "multidict-6.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1535cec6443bfd80d028052e9d17ba6ff8a5a3534c51d285ba56c18af97e9713"}, - {file = "multidict-6.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3b73e7227681f85d19dec46e5b881827cd354aabe46049e1a61d2f9aaa4e285a"}, - {file = "multidict-6.4.3-cp312-cp312-win32.whl", hash = "sha256:8eac0c49df91b88bf91f818e0a24c1c46f3622978e2c27035bfdca98e0e18124"}, - {file = "multidict-6.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:11990b5c757d956cd1db7cb140be50a63216af32cd6506329c2c59d732d802db"}, - {file = "multidict-6.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a76534263d03ae0cfa721fea40fd2b5b9d17a6f85e98025931d41dc49504474"}, - {file = "multidict-6.4.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:805031c2f599eee62ac579843555ed1ce389ae00c7e9f74c2a1b45e0564a88dd"}, - {file = "multidict-6.4.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c56c179839d5dcf51d565132185409d1d5dd8e614ba501eb79023a6cab25576b"}, - {file = "multidict-6.4.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c64f4ddb3886dd8ab71b68a7431ad4aa01a8fa5be5b11543b29674f29ca0ba3"}, - {file = "multidict-6.4.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3002a856367c0b41cad6784f5b8d3ab008eda194ed7864aaa58f65312e2abcac"}, - {file = "multidict-6.4.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d75e621e7d887d539d6e1d789f0c64271c250276c333480a9e1de089611f790"}, - {file = "multidict-6.4.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:995015cf4a3c0d72cbf453b10a999b92c5629eaf3a0c3e1efb4b5c1f602253bb"}, - {file = "multidict-6.4.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2b0fabae7939d09d7d16a711468c385272fa1b9b7fb0d37e51143585d8e72e0"}, - {file = "multidict-6.4.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:61ed4d82f8a1e67eb9eb04f8587970d78fe7cddb4e4d6230b77eda23d27938f9"}, - {file = "multidict-6.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:062428944a8dc69df9fdc5d5fc6279421e5f9c75a9ee3f586f274ba7b05ab3c8"}, - {file = "multidict-6.4.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:b90e27b4674e6c405ad6c64e515a505c6d113b832df52fdacb6b1ffd1fa9a1d1"}, - {file = "multidict-6.4.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7d50d4abf6729921e9613d98344b74241572b751c6b37feed75fb0c37bd5a817"}, - {file = "multidict-6.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:43fe10524fb0a0514be3954be53258e61d87341008ce4914f8e8b92bee6f875d"}, - {file = "multidict-6.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:236966ca6c472ea4e2d3f02f6673ebfd36ba3f23159c323f5a496869bc8e47c9"}, - {file = "multidict-6.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:422a5ec315018e606473ba1f5431e064cf8b2a7468019233dcf8082fabad64c8"}, - {file = "multidict-6.4.3-cp313-cp313-win32.whl", hash = "sha256:f901a5aace8e8c25d78960dcc24c870c8d356660d3b49b93a78bf38eb682aac3"}, - {file = "multidict-6.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:1c152c49e42277bc9a2f7b78bd5fa10b13e88d1b0328221e7aef89d5c60a99a5"}, - {file = "multidict-6.4.3-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:be8751869e28b9c0d368d94f5afcb4234db66fe8496144547b4b6d6a0645cfc6"}, - {file = "multidict-6.4.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0d4b31f8a68dccbcd2c0ea04f0e014f1defc6b78f0eb8b35f2265e8716a6df0c"}, - {file = "multidict-6.4.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:032efeab3049e37eef2ff91271884303becc9e54d740b492a93b7e7266e23756"}, - {file = "multidict-6.4.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e78006af1a7c8a8007e4f56629d7252668344442f66982368ac06522445e375"}, - {file = "multidict-6.4.3-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:daeac9dd30cda8703c417e4fddccd7c4dc0c73421a0b54a7da2713be125846be"}, - {file = "multidict-6.4.3-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f6f90700881438953eae443a9c6f8a509808bc3b185246992c4233ccee37fea"}, - {file = "multidict-6.4.3-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f84627997008390dd15762128dcf73c3365f4ec0106739cde6c20a07ed198ec8"}, - {file = "multidict-6.4.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3307b48cd156153b117c0ea54890a3bdbf858a5b296ddd40dc3852e5f16e9b02"}, - {file = "multidict-6.4.3-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ead46b0fa1dcf5af503a46e9f1c2e80b5d95c6011526352fa5f42ea201526124"}, - {file = "multidict-6.4.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1748cb2743bedc339d63eb1bca314061568793acd603a6e37b09a326334c9f44"}, - {file = "multidict-6.4.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:acc9fa606f76fc111b4569348cc23a771cb52c61516dcc6bcef46d612edb483b"}, - {file = "multidict-6.4.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:31469d5832b5885adeb70982e531ce86f8c992334edd2f2254a10fa3182ac504"}, - {file = "multidict-6.4.3-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ba46b51b6e51b4ef7bfb84b82f5db0dc5e300fb222a8a13b8cd4111898a869cf"}, - {file = "multidict-6.4.3-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:389cfefb599edf3fcfd5f64c0410da686f90f5f5e2c4d84e14f6797a5a337af4"}, - {file = "multidict-6.4.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:64bc2bbc5fba7b9db5c2c8d750824f41c6994e3882e6d73c903c2afa78d091e4"}, - {file = "multidict-6.4.3-cp313-cp313t-win32.whl", hash = "sha256:0ecdc12ea44bab2807d6b4a7e5eef25109ab1c82a8240d86d3c1fc9f3b72efd5"}, - {file = "multidict-6.4.3-cp313-cp313t-win_amd64.whl", hash = "sha256:7146a8742ea71b5d7d955bffcef58a9e6e04efba704b52a460134fefd10a8208"}, - {file = "multidict-6.4.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5427a2679e95a642b7f8b0f761e660c845c8e6fe3141cddd6b62005bd133fc21"}, - {file = "multidict-6.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:24a8caa26521b9ad09732972927d7b45b66453e6ebd91a3c6a46d811eeb7349b"}, - {file = "multidict-6.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6b5a272bc7c36a2cd1b56ddc6bff02e9ce499f9f14ee4a45c45434ef083f2459"}, - {file = "multidict-6.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edf74dc5e212b8c75165b435c43eb0d5e81b6b300a938a4eb82827119115e840"}, - {file = "multidict-6.4.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9f35de41aec4b323c71f54b0ca461ebf694fb48bec62f65221f52e0017955b39"}, - {file = "multidict-6.4.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae93e0ff43b6f6892999af64097b18561691ffd835e21a8348a441e256592e1f"}, - {file = "multidict-6.4.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e3929269e9d7eff905d6971d8b8c85e7dbc72c18fb99c8eae6fe0a152f2e343"}, - {file = "multidict-6.4.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb6214fe1750adc2a1b801a199d64b5a67671bf76ebf24c730b157846d0e90d2"}, - {file = "multidict-6.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d79cf5c0c6284e90f72123f4a3e4add52d6c6ebb4a9054e88df15b8d08444c6"}, - {file = "multidict-6.4.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2427370f4a255262928cd14533a70d9738dfacadb7563bc3b7f704cc2360fc4e"}, - {file = "multidict-6.4.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:fbd8d737867912b6c5f99f56782b8cb81f978a97b4437a1c476de90a3e41c9a1"}, - {file = "multidict-6.4.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0ee1bf613c448997f73fc4efb4ecebebb1c02268028dd4f11f011f02300cf1e8"}, - {file = "multidict-6.4.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:578568c4ba5f2b8abd956baf8b23790dbfdc953e87d5b110bce343b4a54fc9e7"}, - {file = "multidict-6.4.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a059ad6b80de5b84b9fa02a39400319e62edd39d210b4e4f8c4f1243bdac4752"}, - {file = "multidict-6.4.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:dd53893675b729a965088aaadd6a1f326a72b83742b056c1065bdd2e2a42b4df"}, - {file = "multidict-6.4.3-cp39-cp39-win32.whl", hash = "sha256:abcfed2c4c139f25c2355e180bcc077a7cae91eefbb8b3927bb3f836c9586f1f"}, - {file = "multidict-6.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:b1b389ae17296dd739015d5ddb222ee99fd66adeae910de21ac950e00979d897"}, - {file = "multidict-6.4.3-py3-none-any.whl", hash = "sha256:59fe01ee8e2a1e8ceb3f6dbb216b09c8d9f4ef1c22c4fc825d045a147fa2ebc9"}, - {file = "multidict-6.4.3.tar.gz", hash = "sha256:3ada0b058c9f213c5f95ba301f922d402ac234f1111a7d8fd70f1b99f3c281ec"}, + {file = "multidict-6.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2e118a202904623b1d2606d1c8614e14c9444b59d64454b0c355044058066469"}, + {file = "multidict-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a42995bdcaff4e22cb1280ae7752c3ed3fbb398090c6991a2797a4a0e5ed16a9"}, + {file = "multidict-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2261b538145723ca776e55208640fffd7ee78184d223f37c2b40b9edfe0e818a"}, + {file = "multidict-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e5b19f8cd67235fab3e195ca389490415d9fef5a315b1fa6f332925dc924262"}, + {file = "multidict-6.5.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:177b081e4dec67c3320b16b3aa0babc178bbf758553085669382c7ec711e1ec8"}, + {file = "multidict-6.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d30a2cc106a7d116b52ee046207614db42380b62e6b1dd2a50eba47c5ca5eb1"}, + {file = "multidict-6.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a72933bc308d7a64de37f0d51795dbeaceebdfb75454f89035cdfc6a74cfd129"}, + {file = "multidict-6.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96d109e663d032280ef8ef62b50924b2e887d5ddf19e301844a6cb7e91a172a6"}, + {file = "multidict-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b555329c9894332401f03b9a87016f0b707b6fccd4706793ec43b4a639e75869"}, + {file = "multidict-6.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6994bad9d471ef2156f2b6850b51e20ee409c6b9deebc0e57be096be9faffdce"}, + {file = "multidict-6.5.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:b15f817276c96cde9060569023808eec966bd8da56a97e6aa8116f34ddab6534"}, + {file = "multidict-6.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b4bf507c991db535a935b2127cf057a58dbc688c9f309c72080795c63e796f58"}, + {file = "multidict-6.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:60c3f8f13d443426c55f88cf3172547bbc600a86d57fd565458b9259239a6737"}, + {file = "multidict-6.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:a10227168a24420c158747fc201d4279aa9af1671f287371597e2b4f2ff21879"}, + {file = "multidict-6.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e3b1425fe54ccfde66b8cfb25d02be34d5dfd2261a71561ffd887ef4088b4b69"}, + {file = "multidict-6.5.0-cp310-cp310-win32.whl", hash = "sha256:b4e47ef51237841d1087e1e1548071a6ef22e27ed0400c272174fa585277c4b4"}, + {file = "multidict-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:63b3b24fadc7067282c88fae5b2f366d5b3a7c15c021c2838de8c65a50eeefb4"}, + {file = "multidict-6.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:8b2d61afbafc679b7eaf08e9de4fa5d38bd5dc7a9c0a577c9f9588fb49f02dbb"}, + {file = "multidict-6.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8b4bf6bb15a05796a07a248084e3e46e032860c899c7a9b981030e61368dba95"}, + {file = "multidict-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46bb05d50219655c42a4b8fcda9c7ee658a09adbb719c48e65a20284e36328ea"}, + {file = "multidict-6.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:54f524d73f4d54e87e03c98f6af601af4777e4668a52b1bd2ae0a4d6fc7b392b"}, + {file = "multidict-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:529b03600466480ecc502000d62e54f185a884ed4570dee90d9a273ee80e37b5"}, + {file = "multidict-6.5.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:69ad681ad7c93a41ee7005cc83a144b5b34a3838bcf7261e2b5356057b0f78de"}, + {file = "multidict-6.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fe9fada8bc0839466b09fa3f6894f003137942984843ec0c3848846329a36ae"}, + {file = "multidict-6.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f94c6ea6405fcf81baef1e459b209a78cda5442e61b5b7a57ede39d99b5204a0"}, + {file = "multidict-6.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84ca75ad8a39ed75f079a8931435a5b51ee4c45d9b32e1740f99969a5d1cc2ee"}, + {file = "multidict-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be4c08f3a2a6cc42b414496017928d95898964fed84b1b2dace0c9ee763061f9"}, + {file = "multidict-6.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:046a7540cfbb4d5dc846a1fd9843f3ba980c6523f2e0c5b8622b4a5c94138ae6"}, + {file = "multidict-6.5.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:64306121171d988af77d74be0d8c73ee1a69cf6f96aea7fa6030c88f32a152dd"}, + {file = "multidict-6.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b4ac1dd5eb0ecf6f7351d5a9137f30a83f7182209c5d37f61614dfdce5714853"}, + {file = "multidict-6.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bab4a8337235365f4111a7011a1f028826ca683834ebd12de4b85e2844359c36"}, + {file = "multidict-6.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a05b5604c5a75df14a63eeeca598d11b2c3745b9008539b70826ea044063a572"}, + {file = "multidict-6.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:67c4a640952371c9ca65b6a710598be246ef3be5ca83ed38c16a7660d3980877"}, + {file = "multidict-6.5.0-cp311-cp311-win32.whl", hash = "sha256:fdeae096ca36c12d8aca2640b8407a9d94e961372c68435bef14e31cce726138"}, + {file = "multidict-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:e2977ef8b7ce27723ee8c610d1bd1765da4f3fbe5a64f9bf1fd3b4770e31fbc0"}, + {file = "multidict-6.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:82d0cf0ea49bae43d9e8c3851e21954eff716259ff42da401b668744d1760bcb"}, + {file = "multidict-6.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1bb986c8ea9d49947bc325c51eced1ada6d8d9b4c5b15fd3fcdc3c93edef5a74"}, + {file = "multidict-6.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:03c0923da300120830fc467e23805d63bbb4e98b94032bd863bc7797ea5fa653"}, + {file = "multidict-6.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4c78d5ec00fdd35c91680ab5cf58368faad4bd1a8721f87127326270248de9bc"}, + {file = "multidict-6.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadc3cb78be90a887f8f6b73945b840da44b4a483d1c9750459ae69687940c97"}, + {file = "multidict-6.5.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5b02e1ca495d71e07e652e4cef91adae3bf7ae4493507a263f56e617de65dafc"}, + {file = "multidict-6.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7fe92a62326eef351668eec4e2dfc494927764a0840a1895cff16707fceffcd3"}, + {file = "multidict-6.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7673ee4f63879ecd526488deb1989041abcb101b2d30a9165e1e90c489f3f7fb"}, + {file = "multidict-6.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa097ae2a29f573de7e2d86620cbdda5676d27772d4ed2669cfa9961a0d73955"}, + {file = "multidict-6.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:300da0fa4f8457d9c4bd579695496116563409e676ac79b5e4dca18e49d1c308"}, + {file = "multidict-6.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9a19bd108c35877b57393243d392d024cfbfdefe759fd137abb98f6fc910b64c"}, + {file = "multidict-6.5.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f32a1777465a35c35ddbbd7fc1293077938a69402fcc59e40b2846d04a120dd"}, + {file = "multidict-6.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9cc1e10c14ce8112d1e6d8971fe3cdbe13e314f68bea0e727429249d4a6ce164"}, + {file = "multidict-6.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e95c5e07a06594bdc288117ca90e89156aee8cb2d7c330b920d9c3dd19c05414"}, + {file = "multidict-6.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:40ff26f58323795f5cd2855e2718a1720a1123fb90df4553426f0efd76135462"}, + {file = "multidict-6.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:76803a29fd71869a8b59c2118c9dcfb3b8f9c8723e2cce6baeb20705459505cf"}, + {file = "multidict-6.5.0-cp312-cp312-win32.whl", hash = "sha256:df7ecbc65a53a2ce1b3a0c82e6ad1a43dcfe7c6137733f9176a92516b9f5b851"}, + {file = "multidict-6.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ec1c3fbbb0b655a6540bce408f48b9a7474fd94ed657dcd2e890671fefa7743"}, + {file = "multidict-6.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:2d24a00d34808b22c1f15902899b9d82d0faeca9f56281641c791d8605eacd35"}, + {file = "multidict-6.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:53d92df1752df67a928fa7f884aa51edae6f1cf00eeb38cbcf318cf841c17456"}, + {file = "multidict-6.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:680210de2c38eef17ce46b8df8bf2c1ece489261a14a6e43c997d49843a27c99"}, + {file = "multidict-6.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e279259bcb936732bfa1a8eec82b5d2352b3df69d2fa90d25808cfc403cee90a"}, + {file = "multidict-6.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1c185fc1069781e3fc8b622c4331fb3b433979850392daa5efbb97f7f9959bb"}, + {file = "multidict-6.5.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6bb5f65ff91daf19ce97f48f63585e51595539a8a523258b34f7cef2ec7e0617"}, + {file = "multidict-6.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8646b4259450c59b9286db280dd57745897897284f6308edbdf437166d93855"}, + {file = "multidict-6.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d245973d4ecc04eea0a8e5ebec7882cf515480036e1b48e65dffcfbdf86d00be"}, + {file = "multidict-6.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a133e7ddc9bc7fb053733d0ff697ce78c7bf39b5aec4ac12857b6116324c8d75"}, + {file = "multidict-6.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80d696fa38d738fcebfd53eec4d2e3aeb86a67679fd5e53c325756682f152826"}, + {file = "multidict-6.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:20d30c9410ac3908abbaa52ee5967a754c62142043cf2ba091e39681bd51d21a"}, + {file = "multidict-6.5.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:6c65068cc026f217e815fa519d8e959a7188e94ec163ffa029c94ca3ef9d4a73"}, + {file = "multidict-6.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e355ac668a8c3e49c2ca8daa4c92f0ad5b705d26da3d5af6f7d971e46c096da7"}, + {file = "multidict-6.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:08db204213d0375a91a381cae0677ab95dd8c67a465eb370549daf6dbbf8ba10"}, + {file = "multidict-6.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ffa58e3e215af8f6536dc837a990e456129857bb6fd546b3991be470abd9597a"}, + {file = "multidict-6.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3e86eb90015c6f21658dbd257bb8e6aa18bdb365b92dd1fba27ec04e58cdc31b"}, + {file = "multidict-6.5.0-cp313-cp313-win32.whl", hash = "sha256:f34a90fbd9959d0f857323bd3c52b3e6011ed48f78d7d7b9e04980b8a41da3af"}, + {file = "multidict-6.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:fcb2aa79ac6aef8d5b709bbfc2fdb1d75210ba43038d70fbb595b35af470ce06"}, + {file = "multidict-6.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:6dcee5e7e92060b4bb9bb6f01efcbb78c13d0e17d9bc6eec71660dd71dc7b0c2"}, + {file = "multidict-6.5.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:cbbc88abea2388fde41dd574159dec2cda005cb61aa84950828610cb5010f21a"}, + {file = "multidict-6.5.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:70b599f70ae6536e5976364d3c3cf36f40334708bd6cebdd1e2438395d5e7676"}, + {file = "multidict-6.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:828bab777aa8d29d59700018178061854e3a47727e0611cb9bec579d3882de3b"}, + {file = "multidict-6.5.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9695fc1462f17b131c111cf0856a22ff154b0480f86f539d24b2778571ff94d"}, + {file = "multidict-6.5.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b5ac6ebaf5d9814b15f399337ebc6d3a7f4ce9331edd404e76c49a01620b68d"}, + {file = "multidict-6.5.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84a51e3baa77ded07be4766a9e41d977987b97e49884d4c94f6d30ab6acaee14"}, + {file = "multidict-6.5.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8de67f79314d24179e9b1869ed15e88d6ba5452a73fc9891ac142e0ee018b5d6"}, + {file = "multidict-6.5.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17f78a52c214481d30550ec18208e287dfc4736f0c0148208334b105fd9e0887"}, + {file = "multidict-6.5.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2966d0099cb2e2039f9b0e73e7fd5eb9c85805681aa2a7f867f9d95b35356921"}, + {file = "multidict-6.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:86fb42ed5ed1971c642cc52acc82491af97567534a8e381a8d50c02169c4e684"}, + {file = "multidict-6.5.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:4e990cbcb6382f9eae4ec720bcac6a1351509e6fc4a5bb70e4984b27973934e6"}, + {file = "multidict-6.5.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d99a59d64bb1f7f2117bec837d9e534c5aeb5dcedf4c2b16b9753ed28fdc20a3"}, + {file = "multidict-6.5.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:e8ef15cc97c9890212e1caf90f0d63f6560e1e101cf83aeaf63a57556689fb34"}, + {file = "multidict-6.5.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:b8a09aec921b34bd8b9f842f0bcfd76c6a8c033dc5773511e15f2d517e7e1068"}, + {file = "multidict-6.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ff07b504c23b67f2044533244c230808a1258b3493aaf3ea2a0785f70b7be461"}, + {file = "multidict-6.5.0-cp313-cp313t-win32.whl", hash = "sha256:9232a117341e7e979d210e41c04e18f1dc3a1d251268df6c818f5334301274e1"}, + {file = "multidict-6.5.0-cp313-cp313t-win_amd64.whl", hash = "sha256:44cb5c53fb2d4cbcee70a768d796052b75d89b827643788a75ea68189f0980a1"}, + {file = "multidict-6.5.0-cp313-cp313t-win_arm64.whl", hash = "sha256:51d33fafa82640c0217391d4ce895d32b7e84a832b8aee0dcc1b04d8981ec7f4"}, + {file = "multidict-6.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c0078358470da8dc90c37456f4a9cde9f86200949a048d53682b9cd21e5bbf2b"}, + {file = "multidict-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5cc7968b7d1bf8b973c307d38aa3a2f2c783f149bcac855944804252f1df5105"}, + {file = "multidict-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ad73a60e11aa92f1f2c9330efdeaac4531b719fc568eb8d312fd4112f34cc18"}, + {file = "multidict-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3233f21abdcd180b2624eb6988a1e1287210e99bca986d8320afca5005d85844"}, + {file = "multidict-6.5.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:bee5c0b79fca78fd2ab644ca4dc831ecf793eb6830b9f542ee5ed2c91bc35a0e"}, + {file = "multidict-6.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e053a4d690f4352ce46583080fefade9a903ce0fa9d820db1be80bdb9304fa2f"}, + {file = "multidict-6.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42bdee30424c1f4dcda96e07ac60e2a4ede8a89f8ae2f48b5e4ccc060f294c52"}, + {file = "multidict-6.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58b2ded1a7982cf7b8322b0645713a0086b2b3cf5bb9f7c01edfc1a9f98d20dc"}, + {file = "multidict-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f805b8b951d1fadc5bc18c3c93e509608ac5a883045ee33bc22e28806847c20"}, + {file = "multidict-6.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2540395b63723da748f850568357a39cd8d8d4403ca9439f9fcdad6dd423c780"}, + {file = "multidict-6.5.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:c96aedff25f4e47b6697ba048b2c278f7caa6df82c7c3f02e077bcc8d47b4b76"}, + {file = "multidict-6.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e80de5ad995de210fd02a65c2350649b8321d09bd2e44717eaefb0f5814503e8"}, + {file = "multidict-6.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6cb9bcedd9391b313e5ec2fb3aa07c03e050550e7b9e4646c076d5c24ba01532"}, + {file = "multidict-6.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a7d130ed7a112e25ab47309962ecafae07d073316f9d158bc7b3936b52b80121"}, + {file = "multidict-6.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:95750a9a9741cd1855d1b6cb4c6031ae01c01ad38d280217b64bfae986d39d56"}, + {file = "multidict-6.5.0-cp39-cp39-win32.whl", hash = "sha256:7f78caf409914f108f4212b53a9033abfdc2cbab0647e9ac3a25bb0f21ab43d2"}, + {file = "multidict-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:220c74009507e847a3a6fc5375875f2a2e05bd9ce28cf607be0e8c94600f4472"}, + {file = "multidict-6.5.0-cp39-cp39-win_arm64.whl", hash = "sha256:d98f4ac9c1ede7e9d04076e2e6d967e15df0079a6381b297270f6bcab661195e"}, + {file = "multidict-6.5.0-py3-none-any.whl", hash = "sha256:5634b35f225977605385f56153bd95a7133faffc0ffe12ad26e10517537e8dfc"}, + {file = "multidict-6.5.0.tar.gz", hash = "sha256:942bd8002492ba819426a8d7aefde3189c1b87099cdf18aaaefefcf7f3f7b6d2"}, ] [package.dependencies] @@ -1164,110 +1170,110 @@ files = [ [[package]] name = "propcache" -version = "0.3.1" +version = "0.3.2" description = "Accelerated property cache" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "propcache-0.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f27785888d2fdd918bc36de8b8739f2d6c791399552333721b58193f68ea3e98"}, - {file = "propcache-0.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4e89cde74154c7b5957f87a355bb9c8ec929c167b59c83d90654ea36aeb6180"}, - {file = "propcache-0.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:730178f476ef03d3d4d255f0c9fa186cb1d13fd33ffe89d39f2cda4da90ceb71"}, - {file = "propcache-0.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:967a8eec513dbe08330f10137eacb427b2ca52118769e82ebcfcab0fba92a649"}, - {file = "propcache-0.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b9145c35cc87313b5fd480144f8078716007656093d23059e8993d3a8fa730f"}, - {file = "propcache-0.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e64e948ab41411958670f1093c0a57acfdc3bee5cf5b935671bbd5313bcf229"}, - {file = "propcache-0.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:319fa8765bfd6a265e5fa661547556da381e53274bc05094fc9ea50da51bfd46"}, - {file = "propcache-0.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c66d8ccbc902ad548312b96ed8d5d266d0d2c6d006fd0f66323e9d8f2dd49be7"}, - {file = "propcache-0.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2d219b0dbabe75e15e581fc1ae796109b07c8ba7d25b9ae8d650da582bed01b0"}, - {file = "propcache-0.3.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:cd6a55f65241c551eb53f8cf4d2f4af33512c39da5d9777694e9d9c60872f519"}, - {file = "propcache-0.3.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9979643ffc69b799d50d3a7b72b5164a2e97e117009d7af6dfdd2ab906cb72cd"}, - {file = "propcache-0.3.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4cf9e93a81979f1424f1a3d155213dc928f1069d697e4353edb8a5eba67c6259"}, - {file = "propcache-0.3.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2fce1df66915909ff6c824bbb5eb403d2d15f98f1518e583074671a30fe0c21e"}, - {file = "propcache-0.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4d0dfdd9a2ebc77b869a0b04423591ea8823f791293b527dc1bb896c1d6f1136"}, - {file = "propcache-0.3.1-cp310-cp310-win32.whl", hash = "sha256:1f6cc0ad7b4560e5637eb2c994e97b4fa41ba8226069c9277eb5ea7101845b42"}, - {file = "propcache-0.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:47ef24aa6511e388e9894ec16f0fbf3313a53ee68402bc428744a367ec55b833"}, - {file = "propcache-0.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7f30241577d2fef2602113b70ef7231bf4c69a97e04693bde08ddab913ba0ce5"}, - {file = "propcache-0.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:43593c6772aa12abc3af7784bff4a41ffa921608dd38b77cf1dfd7f5c4e71371"}, - {file = "propcache-0.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a75801768bbe65499495660b777e018cbe90c7980f07f8aa57d6be79ea6f71da"}, - {file = "propcache-0.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6f1324db48f001c2ca26a25fa25af60711e09b9aaf4b28488602776f4f9a744"}, - {file = "propcache-0.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cdb0f3e1eb6dfc9965d19734d8f9c481b294b5274337a8cb5cb01b462dcb7e0"}, - {file = "propcache-0.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1eb34d90aac9bfbced9a58b266f8946cb5935869ff01b164573a7634d39fbcb5"}, - {file = "propcache-0.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f35c7070eeec2cdaac6fd3fe245226ed2a6292d3ee8c938e5bb645b434c5f256"}, - {file = "propcache-0.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b23c11c2c9e6d4e7300c92e022046ad09b91fd00e36e83c44483df4afa990073"}, - {file = "propcache-0.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3e19ea4ea0bf46179f8a3652ac1426e6dcbaf577ce4b4f65be581e237340420d"}, - {file = "propcache-0.3.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bd39c92e4c8f6cbf5f08257d6360123af72af9f4da75a690bef50da77362d25f"}, - {file = "propcache-0.3.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b0313e8b923b3814d1c4a524c93dfecea5f39fa95601f6a9b1ac96cd66f89ea0"}, - {file = "propcache-0.3.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e861ad82892408487be144906a368ddbe2dc6297074ade2d892341b35c59844a"}, - {file = "propcache-0.3.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:61014615c1274df8da5991a1e5da85a3ccb00c2d4701ac6f3383afd3ca47ab0a"}, - {file = "propcache-0.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:71ebe3fe42656a2328ab08933d420df5f3ab121772eef78f2dc63624157f0ed9"}, - {file = "propcache-0.3.1-cp311-cp311-win32.whl", hash = "sha256:58aa11f4ca8b60113d4b8e32d37e7e78bd8af4d1a5b5cb4979ed856a45e62005"}, - {file = "propcache-0.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:9532ea0b26a401264b1365146c440a6d78269ed41f83f23818d4b79497aeabe7"}, - {file = "propcache-0.3.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f78eb8422acc93d7b69964012ad7048764bb45a54ba7a39bb9e146c72ea29723"}, - {file = "propcache-0.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:89498dd49c2f9a026ee057965cdf8192e5ae070ce7d7a7bd4b66a8e257d0c976"}, - {file = "propcache-0.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:09400e98545c998d57d10035ff623266927cb784d13dd2b31fd33b8a5316b85b"}, - {file = "propcache-0.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa8efd8c5adc5a2c9d3b952815ff8f7710cefdcaf5f2c36d26aff51aeca2f12f"}, - {file = "propcache-0.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2fe5c910f6007e716a06d269608d307b4f36e7babee5f36533722660e8c4a70"}, - {file = "propcache-0.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a0ab8cf8cdd2194f8ff979a43ab43049b1df0b37aa64ab7eca04ac14429baeb7"}, - {file = "propcache-0.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:563f9d8c03ad645597b8d010ef4e9eab359faeb11a0a2ac9f7b4bc8c28ebef25"}, - {file = "propcache-0.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb6e0faf8cb6b4beea5d6ed7b5a578254c6d7df54c36ccd3d8b3eb00d6770277"}, - {file = "propcache-0.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1c5c7ab7f2bb3f573d1cb921993006ba2d39e8621019dffb1c5bc94cdbae81e8"}, - {file = "propcache-0.3.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:050b571b2e96ec942898f8eb46ea4bfbb19bd5502424747e83badc2d4a99a44e"}, - {file = "propcache-0.3.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e1c4d24b804b3a87e9350f79e2371a705a188d292fd310e663483af6ee6718ee"}, - {file = "propcache-0.3.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e4fe2a6d5ce975c117a6bb1e8ccda772d1e7029c1cca1acd209f91d30fa72815"}, - {file = "propcache-0.3.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:feccd282de1f6322f56f6845bf1207a537227812f0a9bf5571df52bb418d79d5"}, - {file = "propcache-0.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ec314cde7314d2dd0510c6787326bbffcbdc317ecee6b7401ce218b3099075a7"}, - {file = "propcache-0.3.1-cp312-cp312-win32.whl", hash = "sha256:7d2d5a0028d920738372630870e7d9644ce437142197f8c827194fca404bf03b"}, - {file = "propcache-0.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:88c423efef9d7a59dae0614eaed718449c09a5ac79a5f224a8b9664d603f04a3"}, - {file = "propcache-0.3.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f1528ec4374617a7a753f90f20e2f551121bb558fcb35926f99e3c42367164b8"}, - {file = "propcache-0.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dc1915ec523b3b494933b5424980831b636fe483d7d543f7afb7b3bf00f0c10f"}, - {file = "propcache-0.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a110205022d077da24e60b3df8bcee73971be9575dec5573dd17ae5d81751111"}, - {file = "propcache-0.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d249609e547c04d190e820d0d4c8ca03ed4582bcf8e4e160a6969ddfb57b62e5"}, - {file = "propcache-0.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ced33d827625d0a589e831126ccb4f5c29dfdf6766cac441d23995a65825dcb"}, - {file = "propcache-0.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4114c4ada8f3181af20808bedb250da6bae56660e4b8dfd9cd95d4549c0962f7"}, - {file = "propcache-0.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:975af16f406ce48f1333ec5e912fe11064605d5c5b3f6746969077cc3adeb120"}, - {file = "propcache-0.3.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a34aa3a1abc50740be6ac0ab9d594e274f59960d3ad253cd318af76b996dd654"}, - {file = "propcache-0.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9cec3239c85ed15bfaded997773fdad9fb5662b0a7cbc854a43f291eb183179e"}, - {file = "propcache-0.3.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:05543250deac8e61084234d5fc54f8ebd254e8f2b39a16b1dce48904f45b744b"}, - {file = "propcache-0.3.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5cb5918253912e088edbf023788de539219718d3b10aef334476b62d2b53de53"}, - {file = "propcache-0.3.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f3bbecd2f34d0e6d3c543fdb3b15d6b60dd69970c2b4c822379e5ec8f6f621d5"}, - {file = "propcache-0.3.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aca63103895c7d960a5b9b044a83f544b233c95e0dcff114389d64d762017af7"}, - {file = "propcache-0.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a0a9898fdb99bf11786265468571e628ba60af80dc3f6eb89a3545540c6b0ef"}, - {file = "propcache-0.3.1-cp313-cp313-win32.whl", hash = "sha256:3a02a28095b5e63128bcae98eb59025924f121f048a62393db682f049bf4ac24"}, - {file = "propcache-0.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:813fbb8b6aea2fc9659815e585e548fe706d6f663fa73dff59a1677d4595a037"}, - {file = "propcache-0.3.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a444192f20f5ce8a5e52761a031b90f5ea6288b1eef42ad4c7e64fef33540b8f"}, - {file = "propcache-0.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0fbe94666e62ebe36cd652f5fc012abfbc2342de99b523f8267a678e4dfdee3c"}, - {file = "propcache-0.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f011f104db880f4e2166bcdcf7f58250f7a465bc6b068dc84c824a3d4a5c94dc"}, - {file = "propcache-0.3.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e584b6d388aeb0001d6d5c2bd86b26304adde6d9bb9bfa9c4889805021b96de"}, - {file = "propcache-0.3.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a17583515a04358b034e241f952f1715243482fc2c2945fd99a1b03a0bd77d6"}, - {file = "propcache-0.3.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5aed8d8308215089c0734a2af4f2e95eeb360660184ad3912686c181e500b2e7"}, - {file = "propcache-0.3.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d8e309ff9a0503ef70dc9a0ebd3e69cf7b3894c9ae2ae81fc10943c37762458"}, - {file = "propcache-0.3.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b655032b202028a582d27aeedc2e813299f82cb232f969f87a4fde491a233f11"}, - {file = "propcache-0.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f64d91b751df77931336b5ff7bafbe8845c5770b06630e27acd5dbb71e1931c"}, - {file = "propcache-0.3.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:19a06db789a4bd896ee91ebc50d059e23b3639c25d58eb35be3ca1cbe967c3bf"}, - {file = "propcache-0.3.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:bef100c88d8692864651b5f98e871fb090bd65c8a41a1cb0ff2322db39c96c27"}, - {file = "propcache-0.3.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:87380fb1f3089d2a0b8b00f006ed12bd41bd858fabfa7330c954c70f50ed8757"}, - {file = "propcache-0.3.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e474fc718e73ba5ec5180358aa07f6aded0ff5f2abe700e3115c37d75c947e18"}, - {file = "propcache-0.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:17d1c688a443355234f3c031349da69444be052613483f3e4158eef751abcd8a"}, - {file = "propcache-0.3.1-cp313-cp313t-win32.whl", hash = "sha256:359e81a949a7619802eb601d66d37072b79b79c2505e6d3fd8b945538411400d"}, - {file = "propcache-0.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e7fb9a84c9abbf2b2683fa3e7b0d7da4d8ecf139a1c635732a8bda29c5214b0e"}, - {file = "propcache-0.3.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ed5f6d2edbf349bd8d630e81f474d33d6ae5d07760c44d33cd808e2f5c8f4ae6"}, - {file = "propcache-0.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:668ddddc9f3075af019f784456267eb504cb77c2c4bd46cc8402d723b4d200bf"}, - {file = "propcache-0.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0c86e7ceea56376216eba345aa1fc6a8a6b27ac236181f840d1d7e6a1ea9ba5c"}, - {file = "propcache-0.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83be47aa4e35b87c106fc0c84c0fc069d3f9b9b06d3c494cd404ec6747544894"}, - {file = "propcache-0.3.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:27c6ac6aa9fc7bc662f594ef380707494cb42c22786a558d95fcdedb9aa5d035"}, - {file = "propcache-0.3.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64a956dff37080b352c1c40b2966b09defb014347043e740d420ca1eb7c9b908"}, - {file = "propcache-0.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82de5da8c8893056603ac2d6a89eb8b4df49abf1a7c19d536984c8dd63f481d5"}, - {file = "propcache-0.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c3c3a203c375b08fd06a20da3cf7aac293b834b6f4f4db71190e8422750cca5"}, - {file = "propcache-0.3.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b303b194c2e6f171cfddf8b8ba30baefccf03d36a4d9cab7fd0bb68ba476a3d7"}, - {file = "propcache-0.3.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:916cd229b0150129d645ec51614d38129ee74c03293a9f3f17537be0029a9641"}, - {file = "propcache-0.3.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:a461959ead5b38e2581998700b26346b78cd98540b5524796c175722f18b0294"}, - {file = "propcache-0.3.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:069e7212890b0bcf9b2be0a03afb0c2d5161d91e1bf51569a64f629acc7defbf"}, - {file = "propcache-0.3.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ef2e4e91fb3945769e14ce82ed53007195e616a63aa43b40fb7ebaaf907c8d4c"}, - {file = "propcache-0.3.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8638f99dca15b9dff328fb6273e09f03d1c50d9b6512f3b65a4154588a7595fe"}, - {file = "propcache-0.3.1-cp39-cp39-win32.whl", hash = "sha256:6f173bbfe976105aaa890b712d1759de339d8a7cef2fc0a1714cc1a1e1c47f64"}, - {file = "propcache-0.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:603f1fe4144420374f1a69b907494c3acbc867a581c2d49d4175b0de7cc64566"}, - {file = "propcache-0.3.1-py3-none-any.whl", hash = "sha256:9a8ecf38de50a7f518c21568c80f985e776397b902f1ce0b01f799aba1608b40"}, - {file = "propcache-0.3.1.tar.gz", hash = "sha256:40d980c33765359098837527e18eddefc9a24cea5b45e078a7f3bb5b032c6ecf"}, + {file = "propcache-0.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:22d9962a358aedbb7a2e36187ff273adeaab9743373a272976d2e348d08c7770"}, + {file = "propcache-0.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d0fda578d1dc3f77b6b5a5dce3b9ad69a8250a891760a548df850a5e8da87f3"}, + {file = "propcache-0.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3def3da3ac3ce41562d85db655d18ebac740cb3fa4367f11a52b3da9d03a5cc3"}, + {file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bec58347a5a6cebf239daba9bda37dffec5b8d2ce004d9fe4edef3d2815137e"}, + {file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55ffda449a507e9fbd4aca1a7d9aa6753b07d6166140e5a18d2ac9bc49eac220"}, + {file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64a67fb39229a8a8491dd42f864e5e263155e729c2e7ff723d6e25f596b1e8cb"}, + {file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da1cf97b92b51253d5b68cf5a2b9e0dafca095e36b7f2da335e27dc6172a614"}, + {file = "propcache-0.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5f559e127134b07425134b4065be45b166183fdcb433cb6c24c8e4149056ad50"}, + {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aff2e4e06435d61f11a428360a932138d0ec288b0a31dd9bd78d200bd4a2b339"}, + {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4927842833830942a5d0a56e6f4839bc484785b8e1ce8d287359794818633ba0"}, + {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6107ddd08b02654a30fb8ad7a132021759d750a82578b94cd55ee2772b6ebea2"}, + {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:70bd8b9cd6b519e12859c99f3fc9a93f375ebd22a50296c3a295028bea73b9e7"}, + {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2183111651d710d3097338dd1893fcf09c9f54e27ff1a8795495a16a469cc90b"}, + {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fb075ad271405dcad8e2a7ffc9a750a3bf70e533bd86e89f0603e607b93aa64c"}, + {file = "propcache-0.3.2-cp310-cp310-win32.whl", hash = "sha256:404d70768080d3d3bdb41d0771037da19d8340d50b08e104ca0e7f9ce55fce70"}, + {file = "propcache-0.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:7435d766f978b4ede777002e6b3b6641dd229cd1da8d3d3106a45770365f9ad9"}, + {file = "propcache-0.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0b8d2f607bd8f80ddc04088bc2a037fdd17884a6fcadc47a96e334d72f3717be"}, + {file = "propcache-0.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06766d8f34733416e2e34f46fea488ad5d60726bb9481d3cddf89a6fa2d9603f"}, + {file = "propcache-0.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2dc1f4a1df4fecf4e6f68013575ff4af84ef6f478fe5344317a65d38a8e6dc9"}, + {file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be29c4f4810c5789cf10ddf6af80b041c724e629fa51e308a7a0fb19ed1ef7bf"}, + {file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59d61f6970ecbd8ff2e9360304d5c8876a6abd4530cb752c06586849ac8a9dc9"}, + {file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62180e0b8dbb6b004baec00a7983e4cc52f5ada9cd11f48c3528d8cfa7b96a66"}, + {file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c144ca294a204c470f18cf4c9d78887810d04a3e2fbb30eea903575a779159df"}, + {file = "propcache-0.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5c2a784234c28854878d68978265617aa6dc0780e53d44b4d67f3651a17a9a2"}, + {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5745bc7acdafa978ca1642891b82c19238eadc78ba2aaa293c6863b304e552d7"}, + {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c0075bf773d66fa8c9d41f66cc132ecc75e5bb9dd7cce3cfd14adc5ca184cb95"}, + {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5f57aa0847730daceff0497f417c9de353c575d8da3579162cc74ac294c5369e"}, + {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:eef914c014bf72d18efb55619447e0aecd5fb7c2e3fa7441e2e5d6099bddff7e"}, + {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2a4092e8549031e82facf3decdbc0883755d5bbcc62d3aea9d9e185549936dcf"}, + {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:85871b050f174bc0bfb437efbdb68aaf860611953ed12418e4361bc9c392749e"}, + {file = "propcache-0.3.2-cp311-cp311-win32.whl", hash = "sha256:36c8d9b673ec57900c3554264e630d45980fd302458e4ac801802a7fd2ef7897"}, + {file = "propcache-0.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53af8cb6a781b02d2ea079b5b853ba9430fcbe18a8e3ce647d5982a3ff69f39"}, + {file = "propcache-0.3.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8de106b6c84506b31c27168582cd3cb3000a6412c16df14a8628e5871ff83c10"}, + {file = "propcache-0.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:28710b0d3975117239c76600ea351934ac7b5ff56e60953474342608dbbb6154"}, + {file = "propcache-0.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce26862344bdf836650ed2487c3d724b00fbfec4233a1013f597b78c1cb73615"}, + {file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca54bd347a253af2cf4544bbec232ab982f4868de0dd684246b67a51bc6b1db"}, + {file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55780d5e9a2ddc59711d727226bb1ba83a22dd32f64ee15594b9392b1f544eb1"}, + {file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:035e631be25d6975ed87ab23153db6a73426a48db688070d925aa27e996fe93c"}, + {file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee6f22b6eaa39297c751d0e80c0d3a454f112f5c6481214fcf4c092074cecd67"}, + {file = "propcache-0.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ca3aee1aa955438c4dba34fc20a9f390e4c79967257d830f137bd5a8a32ed3b"}, + {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7a4f30862869fa2b68380d677cc1c5fcf1e0f2b9ea0cf665812895c75d0ca3b8"}, + {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b77ec3c257d7816d9f3700013639db7491a434644c906a2578a11daf13176251"}, + {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cab90ac9d3f14b2d5050928483d3d3b8fb6b4018893fc75710e6aa361ecb2474"}, + {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0b504d29f3c47cf6b9e936c1852246c83d450e8e063d50562115a6be6d3a2535"}, + {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:ce2ac2675a6aa41ddb2a0c9cbff53780a617ac3d43e620f8fd77ba1c84dcfc06"}, + {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:62b4239611205294cc433845b914131b2a1f03500ff3c1ed093ed216b82621e1"}, + {file = "propcache-0.3.2-cp312-cp312-win32.whl", hash = "sha256:df4a81b9b53449ebc90cc4deefb052c1dd934ba85012aa912c7ea7b7e38b60c1"}, + {file = "propcache-0.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7046e79b989d7fe457bb755844019e10f693752d169076138abf17f31380800c"}, + {file = "propcache-0.3.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ca592ed634a73ca002967458187109265e980422116c0a107cf93d81f95af945"}, + {file = "propcache-0.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9ecb0aad4020e275652ba3975740f241bd12a61f1a784df044cf7477a02bc252"}, + {file = "propcache-0.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f08f1cc28bd2eade7a8a3d2954ccc673bb02062e3e7da09bc75d843386b342f"}, + {file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1a342c834734edb4be5ecb1e9fb48cb64b1e2320fccbd8c54bf8da8f2a84c33"}, + {file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a544caaae1ac73f1fecfae70ded3e93728831affebd017d53449e3ac052ac1e"}, + {file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:310d11aa44635298397db47a3ebce7db99a4cc4b9bbdfcf6c98a60c8d5261cf1"}, + {file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1396592321ac83157ac03a2023aa6cc4a3cc3cfdecb71090054c09e5a7cce3"}, + {file = "propcache-0.3.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cabf5b5902272565e78197edb682017d21cf3b550ba0460ee473753f28d23c1"}, + {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0a2f2235ac46a7aa25bdeb03a9e7060f6ecbd213b1f9101c43b3090ffb971ef6"}, + {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:92b69e12e34869a6970fd2f3da91669899994b47c98f5d430b781c26f1d9f387"}, + {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:54e02207c79968ebbdffc169591009f4474dde3b4679e16634d34c9363ff56b4"}, + {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4adfb44cb588001f68c5466579d3f1157ca07f7504fc91ec87862e2b8e556b88"}, + {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fd3e6019dc1261cd0291ee8919dd91fbab7b169bb76aeef6c716833a3f65d206"}, + {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c181cad81158d71c41a2bce88edce078458e2dd5ffee7eddd6b05da85079f43"}, + {file = "propcache-0.3.2-cp313-cp313-win32.whl", hash = "sha256:8a08154613f2249519e549de2330cf8e2071c2887309a7b07fb56098f5170a02"}, + {file = "propcache-0.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e41671f1594fc4ab0a6dec1351864713cb3a279910ae8b58f884a88a0a632c05"}, + {file = "propcache-0.3.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:9a3cf035bbaf035f109987d9d55dc90e4b0e36e04bbbb95af3055ef17194057b"}, + {file = "propcache-0.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:156c03d07dc1323d8dacaa221fbe028c5c70d16709cdd63502778e6c3ccca1b0"}, + {file = "propcache-0.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74413c0ba02ba86f55cf60d18daab219f7e531620c15f1e23d95563f505efe7e"}, + {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f066b437bb3fa39c58ff97ab2ca351db465157d68ed0440abecb21715eb24b28"}, + {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1304b085c83067914721e7e9d9917d41ad87696bf70f0bc7dee450e9c71ad0a"}, + {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab50cef01b372763a13333b4e54021bdcb291fc9a8e2ccb9c2df98be51bcde6c"}, + {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad3b2a085ec259ad2c2842666b2a0a49dea8463579c606426128925af1ed725"}, + {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:261fa020c1c14deafd54c76b014956e2f86991af198c51139faf41c4d5e83892"}, + {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:46d7f8aa79c927e5f987ee3a80205c987717d3659f035c85cf0c3680526bdb44"}, + {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:6d8f3f0eebf73e3c0ff0e7853f68be638b4043c65a70517bb575eff54edd8dbe"}, + {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:03c89c1b14a5452cf15403e291c0ccd7751d5b9736ecb2c5bab977ad6c5bcd81"}, + {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:0cc17efde71e12bbaad086d679ce575268d70bc123a5a71ea7ad76f70ba30bba"}, + {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:acdf05d00696bc0447e278bb53cb04ca72354e562cf88ea6f9107df8e7fd9770"}, + {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4445542398bd0b5d32df908031cb1b30d43ac848e20470a878b770ec2dcc6330"}, + {file = "propcache-0.3.2-cp313-cp313t-win32.whl", hash = "sha256:f86e5d7cd03afb3a1db8e9f9f6eff15794e79e791350ac48a8c924e6f439f394"}, + {file = "propcache-0.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9704bedf6e7cbe3c65eca4379a9b53ee6a83749f047808cbb5044d40d7d72198"}, + {file = "propcache-0.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a7fad897f14d92086d6b03fdd2eb844777b0c4d7ec5e3bac0fbae2ab0602bbe5"}, + {file = "propcache-0.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1f43837d4ca000243fd7fd6301947d7cb93360d03cd08369969450cc6b2ce3b4"}, + {file = "propcache-0.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:261df2e9474a5949c46e962065d88eb9b96ce0f2bd30e9d3136bcde84befd8f2"}, + {file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e514326b79e51f0a177daab1052bc164d9d9e54133797a3a58d24c9c87a3fe6d"}, + {file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4a996adb6904f85894570301939afeee65f072b4fd265ed7e569e8d9058e4ec"}, + {file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:76cace5d6b2a54e55b137669b30f31aa15977eeed390c7cbfb1dafa8dfe9a701"}, + {file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31248e44b81d59d6addbb182c4720f90b44e1efdc19f58112a3c3a1615fb47ef"}, + {file = "propcache-0.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abb7fa19dbf88d3857363e0493b999b8011eea856b846305d8c0512dfdf8fbb1"}, + {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d81ac3ae39d38588ad0549e321e6f773a4e7cc68e7751524a22885d5bbadf886"}, + {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:cc2782eb0f7a16462285b6f8394bbbd0e1ee5f928034e941ffc444012224171b"}, + {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:db429c19a6c7e8a1c320e6a13c99799450f411b02251fb1b75e6217cf4a14fcb"}, + {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:21d8759141a9e00a681d35a1f160892a36fb6caa715ba0b832f7747da48fb6ea"}, + {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2ca6d378f09adb13837614ad2754fa8afaee330254f404299611bce41a8438cb"}, + {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:34a624af06c048946709f4278b4176470073deda88d91342665d95f7c6270fbe"}, + {file = "propcache-0.3.2-cp39-cp39-win32.whl", hash = "sha256:4ba3fef1c30f306b1c274ce0b8baaa2c3cdd91f645c48f06394068f37d3837a1"}, + {file = "propcache-0.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:7a2368eed65fc69a7a7a40b27f22e85e7627b74216f0846b04ba5c116e191ec9"}, + {file = "propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f"}, + {file = "propcache-0.3.2.tar.gz", hash = "sha256:20d7d62e4e7ef05f221e0db2856b979540686342e7dd9973b815599c7057e168"}, ] [[package]] @@ -1621,15 +1627,15 @@ keyring = ["keyring (>=15.1)"] [[package]] name = "typing-extensions" -version = "4.13.2" -description = "Backported and Experimental Type Hints for Python 3.8+" +version = "4.14.0" +description = "Backported and Experimental Type Hints for Python 3.9+" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main", "dev"] markers = "python_version < \"3.11\"" files = [ - {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, - {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, + {file = "typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af"}, + {file = "typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4"}, ] [[package]] @@ -1669,116 +1675,116 @@ test = ["websockets"] [[package]] name = "yarl" -version = "1.20.0" +version = "1.20.1" description = "Yet another URL library" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "yarl-1.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f1f6670b9ae3daedb325fa55fbe31c22c8228f6e0b513772c2e1c623caa6ab22"}, - {file = "yarl-1.20.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85a231fa250dfa3308f3c7896cc007a47bc76e9e8e8595c20b7426cac4884c62"}, - {file = "yarl-1.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a06701b647c9939d7019acdfa7ebbfbb78ba6aa05985bb195ad716ea759a569"}, - {file = "yarl-1.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7595498d085becc8fb9203aa314b136ab0516c7abd97e7d74f7bb4eb95042abe"}, - {file = "yarl-1.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af5607159085dcdb055d5678fc2d34949bd75ae6ea6b4381e784bbab1c3aa195"}, - {file = "yarl-1.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:95b50910e496567434cb77a577493c26bce0f31c8a305135f3bda6a2483b8e10"}, - {file = "yarl-1.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b594113a301ad537766b4e16a5a6750fcbb1497dcc1bc8a4daae889e6402a634"}, - {file = "yarl-1.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:083ce0393ea173cd37834eb84df15b6853b555d20c52703e21fbababa8c129d2"}, - {file = "yarl-1.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f1a350a652bbbe12f666109fbddfdf049b3ff43696d18c9ab1531fbba1c977a"}, - {file = "yarl-1.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fb0caeac4a164aadce342f1597297ec0ce261ec4532bbc5a9ca8da5622f53867"}, - {file = "yarl-1.20.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:d88cc43e923f324203f6ec14434fa33b85c06d18d59c167a0637164863b8e995"}, - {file = "yarl-1.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e52d6ed9ea8fd3abf4031325dc714aed5afcbfa19ee4a89898d663c9976eb487"}, - {file = "yarl-1.20.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ce360ae48a5e9961d0c730cf891d40698a82804e85f6e74658fb175207a77cb2"}, - {file = "yarl-1.20.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:06d06c9d5b5bc3eb56542ceeba6658d31f54cf401e8468512447834856fb0e61"}, - {file = "yarl-1.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c27d98f4e5c4060582f44e58309c1e55134880558f1add7a87c1bc36ecfade19"}, - {file = "yarl-1.20.0-cp310-cp310-win32.whl", hash = "sha256:f4d3fa9b9f013f7050326e165c3279e22850d02ae544ace285674cb6174b5d6d"}, - {file = "yarl-1.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:bc906b636239631d42eb8a07df8359905da02704a868983265603887ed68c076"}, - {file = "yarl-1.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fdb5204d17cb32b2de2d1e21c7461cabfacf17f3645e4b9039f210c5d3378bf3"}, - {file = "yarl-1.20.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:eaddd7804d8e77d67c28d154ae5fab203163bd0998769569861258e525039d2a"}, - {file = "yarl-1.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:634b7ba6b4a85cf67e9df7c13a7fb2e44fa37b5d34501038d174a63eaac25ee2"}, - {file = "yarl-1.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d409e321e4addf7d97ee84162538c7258e53792eb7c6defd0c33647d754172e"}, - {file = "yarl-1.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ea52f7328a36960ba3231c6677380fa67811b414798a6e071c7085c57b6d20a9"}, - {file = "yarl-1.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c8703517b924463994c344dcdf99a2d5ce9eca2b6882bb640aa555fb5efc706a"}, - {file = "yarl-1.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:077989b09ffd2f48fb2d8f6a86c5fef02f63ffe6b1dd4824c76de7bb01e4f2e2"}, - {file = "yarl-1.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0acfaf1da020253f3533526e8b7dd212838fdc4109959a2c53cafc6db611bff2"}, - {file = "yarl-1.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4230ac0b97ec5eeb91d96b324d66060a43fd0d2a9b603e3327ed65f084e41f8"}, - {file = "yarl-1.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a6a1e6ae21cdd84011c24c78d7a126425148b24d437b5702328e4ba640a8902"}, - {file = "yarl-1.20.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:86de313371ec04dd2531f30bc41a5a1a96f25a02823558ee0f2af0beaa7ca791"}, - {file = "yarl-1.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:dd59c9dd58ae16eaa0f48c3d0cbe6be8ab4dc7247c3ff7db678edecbaf59327f"}, - {file = "yarl-1.20.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a0bc5e05f457b7c1994cc29e83b58f540b76234ba6b9648a4971ddc7f6aa52da"}, - {file = "yarl-1.20.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c9471ca18e6aeb0e03276b5e9b27b14a54c052d370a9c0c04a68cefbd1455eb4"}, - {file = "yarl-1.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:40ed574b4df723583a26c04b298b283ff171bcc387bc34c2683235e2487a65a5"}, - {file = "yarl-1.20.0-cp311-cp311-win32.whl", hash = "sha256:db243357c6c2bf3cd7e17080034ade668d54ce304d820c2a58514a4e51d0cfd6"}, - {file = "yarl-1.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:8c12cd754d9dbd14204c328915e23b0c361b88f3cffd124129955e60a4fbfcfb"}, - {file = "yarl-1.20.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e06b9f6cdd772f9b665e5ba8161968e11e403774114420737f7884b5bd7bdf6f"}, - {file = "yarl-1.20.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b9ae2fbe54d859b3ade40290f60fe40e7f969d83d482e84d2c31b9bff03e359e"}, - {file = "yarl-1.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6d12b8945250d80c67688602c891237994d203d42427cb14e36d1a732eda480e"}, - {file = "yarl-1.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:087e9731884621b162a3e06dc0d2d626e1542a617f65ba7cc7aeab279d55ad33"}, - {file = "yarl-1.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:69df35468b66c1a6e6556248e6443ef0ec5f11a7a4428cf1f6281f1879220f58"}, - {file = "yarl-1.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b2992fe29002fd0d4cbaea9428b09af9b8686a9024c840b8a2b8f4ea4abc16f"}, - {file = "yarl-1.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4c903e0b42aab48abfbac668b5a9d7b6938e721a6341751331bcd7553de2dcae"}, - {file = "yarl-1.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf099e2432131093cc611623e0b0bcc399b8cddd9a91eded8bfb50402ec35018"}, - {file = "yarl-1.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a7f62f5dc70a6c763bec9ebf922be52aa22863d9496a9a30124d65b489ea672"}, - {file = "yarl-1.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:54ac15a8b60382b2bcefd9a289ee26dc0920cf59b05368c9b2b72450751c6eb8"}, - {file = "yarl-1.20.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:25b3bc0763a7aca16a0f1b5e8ef0f23829df11fb539a1b70476dcab28bd83da7"}, - {file = "yarl-1.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b2586e36dc070fc8fad6270f93242124df68b379c3a251af534030a4a33ef594"}, - {file = "yarl-1.20.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:866349da9d8c5290cfefb7fcc47721e94de3f315433613e01b435473be63daa6"}, - {file = "yarl-1.20.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:33bb660b390a0554d41f8ebec5cd4475502d84104b27e9b42f5321c5192bfcd1"}, - {file = "yarl-1.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:737e9f171e5a07031cbee5e9180f6ce21a6c599b9d4b2c24d35df20a52fabf4b"}, - {file = "yarl-1.20.0-cp312-cp312-win32.whl", hash = "sha256:839de4c574169b6598d47ad61534e6981979ca2c820ccb77bf70f4311dd2cc64"}, - {file = "yarl-1.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:3d7dbbe44b443b0c4aa0971cb07dcb2c2060e4a9bf8d1301140a33a93c98e18c"}, - {file = "yarl-1.20.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2137810a20b933b1b1b7e5cf06a64c3ed3b4747b0e5d79c9447c00db0e2f752f"}, - {file = "yarl-1.20.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:447c5eadd750db8389804030d15f43d30435ed47af1313303ed82a62388176d3"}, - {file = "yarl-1.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42fbe577272c203528d402eec8bf4b2d14fd49ecfec92272334270b850e9cd7d"}, - {file = "yarl-1.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18e321617de4ab170226cd15006a565d0fa0d908f11f724a2c9142d6b2812ab0"}, - {file = "yarl-1.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4345f58719825bba29895011e8e3b545e6e00257abb984f9f27fe923afca2501"}, - {file = "yarl-1.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d9b980d7234614bc4674468ab173ed77d678349c860c3af83b1fffb6a837ddc"}, - {file = "yarl-1.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af4baa8a445977831cbaa91a9a84cc09debb10bc8391f128da2f7bd070fc351d"}, - {file = "yarl-1.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:123393db7420e71d6ce40d24885a9e65eb1edefc7a5228db2d62bcab3386a5c0"}, - {file = "yarl-1.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ab47acc9332f3de1b39e9b702d9c916af7f02656b2a86a474d9db4e53ef8fd7a"}, - {file = "yarl-1.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4a34c52ed158f89876cba9c600b2c964dfc1ca52ba7b3ab6deb722d1d8be6df2"}, - {file = "yarl-1.20.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:04d8cfb12714158abf2618f792c77bc5c3d8c5f37353e79509608be4f18705c9"}, - {file = "yarl-1.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7dc63ad0d541c38b6ae2255aaa794434293964677d5c1ec5d0116b0e308031f5"}, - {file = "yarl-1.20.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d02b591a64e4e6ca18c5e3d925f11b559c763b950184a64cf47d74d7e41877"}, - {file = "yarl-1.20.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:95fc9876f917cac7f757df80a5dda9de59d423568460fe75d128c813b9af558e"}, - {file = "yarl-1.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bb769ae5760cd1c6a712135ee7915f9d43f11d9ef769cb3f75a23e398a92d384"}, - {file = "yarl-1.20.0-cp313-cp313-win32.whl", hash = "sha256:70e0c580a0292c7414a1cead1e076c9786f685c1fc4757573d2967689b370e62"}, - {file = "yarl-1.20.0-cp313-cp313-win_amd64.whl", hash = "sha256:4c43030e4b0af775a85be1fa0433119b1565673266a70bf87ef68a9d5ba3174c"}, - {file = "yarl-1.20.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b6c4c3d0d6a0ae9b281e492b1465c72de433b782e6b5001c8e7249e085b69051"}, - {file = "yarl-1.20.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8681700f4e4df891eafa4f69a439a6e7d480d64e52bf460918f58e443bd3da7d"}, - {file = "yarl-1.20.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:84aeb556cb06c00652dbf87c17838eb6d92cfd317799a8092cee0e570ee11229"}, - {file = "yarl-1.20.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f166eafa78810ddb383e930d62e623d288fb04ec566d1b4790099ae0f31485f1"}, - {file = "yarl-1.20.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5d3d6d14754aefc7a458261027a562f024d4f6b8a798adb472277f675857b1eb"}, - {file = "yarl-1.20.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a8f64df8ed5d04c51260dbae3cc82e5649834eebea9eadfd829837b8093eb00"}, - {file = "yarl-1.20.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4d9949eaf05b4d30e93e4034a7790634bbb41b8be2d07edd26754f2e38e491de"}, - {file = "yarl-1.20.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c366b254082d21cc4f08f522ac201d0d83a8b8447ab562732931d31d80eb2a5"}, - {file = "yarl-1.20.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91bc450c80a2e9685b10e34e41aef3d44ddf99b3a498717938926d05ca493f6a"}, - {file = "yarl-1.20.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9c2aa4387de4bc3a5fe158080757748d16567119bef215bec643716b4fbf53f9"}, - {file = "yarl-1.20.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:d2cbca6760a541189cf87ee54ff891e1d9ea6406079c66341008f7ef6ab61145"}, - {file = "yarl-1.20.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:798a5074e656f06b9fad1a162be5a32da45237ce19d07884d0b67a0aa9d5fdda"}, - {file = "yarl-1.20.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:f106e75c454288472dbe615accef8248c686958c2e7dd3b8d8ee2669770d020f"}, - {file = "yarl-1.20.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:3b60a86551669c23dc5445010534d2c5d8a4e012163218fc9114e857c0586fdd"}, - {file = "yarl-1.20.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3e429857e341d5e8e15806118e0294f8073ba9c4580637e59ab7b238afca836f"}, - {file = "yarl-1.20.0-cp313-cp313t-win32.whl", hash = "sha256:65a4053580fe88a63e8e4056b427224cd01edfb5f951498bfefca4052f0ce0ac"}, - {file = "yarl-1.20.0-cp313-cp313t-win_amd64.whl", hash = "sha256:53b2da3a6ca0a541c1ae799c349788d480e5144cac47dba0266c7cb6c76151fe"}, - {file = "yarl-1.20.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:119bca25e63a7725b0c9d20ac67ca6d98fa40e5a894bd5d4686010ff73397914"}, - {file = "yarl-1.20.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:35d20fb919546995f1d8c9e41f485febd266f60e55383090010f272aca93edcc"}, - {file = "yarl-1.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:484e7a08f72683c0f160270566b4395ea5412b4359772b98659921411d32ad26"}, - {file = "yarl-1.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d8a3d54a090e0fff5837cd3cc305dd8a07d3435a088ddb1f65e33b322f66a94"}, - {file = "yarl-1.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f0cf05ae2d3d87a8c9022f3885ac6dea2b751aefd66a4f200e408a61ae9b7f0d"}, - {file = "yarl-1.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a884b8974729e3899d9287df46f015ce53f7282d8d3340fa0ed57536b440621c"}, - {file = "yarl-1.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f8d8aa8dd89ffb9a831fedbcb27d00ffd9f4842107d52dc9d57e64cb34073d5c"}, - {file = "yarl-1.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b4e88d6c3c8672f45a30867817e4537df1bbc6f882a91581faf1f6d9f0f1b5a"}, - {file = "yarl-1.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdb77efde644d6f1ad27be8a5d67c10b7f769804fff7a966ccb1da5a4de4b656"}, - {file = "yarl-1.20.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4ba5e59f14bfe8d261a654278a0f6364feef64a794bd456a8c9e823071e5061c"}, - {file = "yarl-1.20.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:d0bf955b96ea44ad914bc792c26a0edcd71b4668b93cbcd60f5b0aeaaed06c64"}, - {file = "yarl-1.20.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:27359776bc359ee6eaefe40cb19060238f31228799e43ebd3884e9c589e63b20"}, - {file = "yarl-1.20.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:04d9c7a1dc0a26efb33e1acb56c8849bd57a693b85f44774356c92d610369efa"}, - {file = "yarl-1.20.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:faa709b66ae0e24c8e5134033187a972d849d87ed0a12a0366bedcc6b5dc14a5"}, - {file = "yarl-1.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:44869ee8538208fe5d9342ed62c11cc6a7a1af1b3d0bb79bb795101b6e77f6e0"}, - {file = "yarl-1.20.0-cp39-cp39-win32.whl", hash = "sha256:b7fa0cb9fd27ffb1211cde944b41f5c67ab1c13a13ebafe470b1e206b8459da8"}, - {file = "yarl-1.20.0-cp39-cp39-win_amd64.whl", hash = "sha256:d4fad6e5189c847820288286732075f213eabf81be4d08d6cc309912e62be5b7"}, - {file = "yarl-1.20.0-py3-none-any.whl", hash = "sha256:5d0fe6af927a47a230f31e6004621fd0959eaa915fc62acfafa67ff7229a3124"}, - {file = "yarl-1.20.0.tar.gz", hash = "sha256:686d51e51ee5dfe62dec86e4866ee0e9ed66df700d55c828a615640adc885307"}, + {file = "yarl-1.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6032e6da6abd41e4acda34d75a816012717000fa6839f37124a47fcefc49bec4"}, + {file = "yarl-1.20.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c7b34d804b8cf9b214f05015c4fee2ebe7ed05cf581e7192c06555c71f4446a"}, + {file = "yarl-1.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c869f2651cc77465f6cd01d938d91a11d9ea5d798738c1dc077f3de0b5e5fed"}, + {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62915e6688eb4d180d93840cda4110995ad50c459bf931b8b3775b37c264af1e"}, + {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:41ebd28167bc6af8abb97fec1a399f412eec5fd61a3ccbe2305a18b84fb4ca73"}, + {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21242b4288a6d56f04ea193adde174b7e347ac46ce6bc84989ff7c1b1ecea84e"}, + {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bea21cdae6c7eb02ba02a475f37463abfe0a01f5d7200121b03e605d6a0439f8"}, + {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f8a891e4a22a89f5dde7862994485e19db246b70bb288d3ce73a34422e55b23"}, + {file = "yarl-1.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd803820d44c8853a109a34e3660e5a61beae12970da479cf44aa2954019bf70"}, + {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b982fa7f74c80d5c0c7b5b38f908971e513380a10fecea528091405f519b9ebb"}, + {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:33f29ecfe0330c570d997bcf1afd304377f2e48f61447f37e846a6058a4d33b2"}, + {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:835ab2cfc74d5eb4a6a528c57f05688099da41cf4957cf08cad38647e4a83b30"}, + {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:46b5e0ccf1943a9a6e766b2c2b8c732c55b34e28be57d8daa2b3c1d1d4009309"}, + {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:df47c55f7d74127d1b11251fe6397d84afdde0d53b90bedb46a23c0e534f9d24"}, + {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76d12524d05841276b0e22573f28d5fbcb67589836772ae9244d90dd7d66aa13"}, + {file = "yarl-1.20.1-cp310-cp310-win32.whl", hash = "sha256:6c4fbf6b02d70e512d7ade4b1f998f237137f1417ab07ec06358ea04f69134f8"}, + {file = "yarl-1.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:aef6c4d69554d44b7f9d923245f8ad9a707d971e6209d51279196d8e8fe1ae16"}, + {file = "yarl-1.20.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47ee6188fea634bdfaeb2cc420f5b3b17332e6225ce88149a17c413c77ff269e"}, + {file = "yarl-1.20.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0f6500f69e8402d513e5eedb77a4e1818691e8f45e6b687147963514d84b44b"}, + {file = "yarl-1.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a8900a42fcdaad568de58887c7b2f602962356908eedb7628eaf6021a6e435b"}, + {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bad6d131fda8ef508b36be3ece16d0902e80b88ea7200f030a0f6c11d9e508d4"}, + {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:df018d92fe22aaebb679a7f89fe0c0f368ec497e3dda6cb81a567610f04501f1"}, + {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f969afbb0a9b63c18d0feecf0db09d164b7a44a053e78a7d05f5df163e43833"}, + {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:812303eb4aa98e302886ccda58d6b099e3576b1b9276161469c25803a8db277d"}, + {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98c4a7d166635147924aa0bf9bfe8d8abad6fffa6102de9c99ea04a1376f91e8"}, + {file = "yarl-1.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12e768f966538e81e6e7550f9086a6236b16e26cd964cf4df35349970f3551cf"}, + {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe41919b9d899661c5c28a8b4b0acf704510b88f27f0934ac7a7bebdd8938d5e"}, + {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8601bc010d1d7780592f3fc1bdc6c72e2b6466ea34569778422943e1a1f3c389"}, + {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:daadbdc1f2a9033a2399c42646fbd46da7992e868a5fe9513860122d7fe7a73f"}, + {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:03aa1e041727cb438ca762628109ef1333498b122e4c76dd858d186a37cec845"}, + {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:642980ef5e0fa1de5fa96d905c7e00cb2c47cb468bfcac5a18c58e27dbf8d8d1"}, + {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:86971e2795584fe8c002356d3b97ef6c61862720eeff03db2a7c86b678d85b3e"}, + {file = "yarl-1.20.1-cp311-cp311-win32.whl", hash = "sha256:597f40615b8d25812f14562699e287f0dcc035d25eb74da72cae043bb884d773"}, + {file = "yarl-1.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:26ef53a9e726e61e9cd1cda6b478f17e350fb5800b4bd1cd9fe81c4d91cfeb2e"}, + {file = "yarl-1.20.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdcc4cd244e58593a4379fe60fdee5ac0331f8eb70320a24d591a3be197b94a9"}, + {file = "yarl-1.20.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b29a2c385a5f5b9c7d9347e5812b6f7ab267193c62d282a540b4fc528c8a9d2a"}, + {file = "yarl-1.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1112ae8154186dfe2de4732197f59c05a83dc814849a5ced892b708033f40dc2"}, + {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90bbd29c4fe234233f7fa2b9b121fb63c321830e5d05b45153a2ca68f7d310ee"}, + {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:680e19c7ce3710ac4cd964e90dad99bf9b5029372ba0c7cbfcd55e54d90ea819"}, + {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a979218c1fdb4246a05efc2cc23859d47c89af463a90b99b7c56094daf25a16"}, + {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255b468adf57b4a7b65d8aad5b5138dce6a0752c139965711bdcb81bc370e1b6"}, + {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a97d67108e79cfe22e2b430d80d7571ae57d19f17cda8bb967057ca8a7bf5bfd"}, + {file = "yarl-1.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8570d998db4ddbfb9a590b185a0a33dbf8aafb831d07a5257b4ec9948df9cb0a"}, + {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:97c75596019baae7c71ccf1d8cc4738bc08134060d0adfcbe5642f778d1dca38"}, + {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1c48912653e63aef91ff988c5432832692ac5a1d8f0fb8a33091520b5bbe19ef"}, + {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4c3ae28f3ae1563c50f3d37f064ddb1511ecc1d5584e88c6b7c63cf7702a6d5f"}, + {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c5e9642f27036283550f5f57dc6156c51084b458570b9d0d96100c8bebb186a8"}, + {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2c26b0c49220d5799f7b22c6838409ee9bc58ee5c95361a4d7831f03cc225b5a"}, + {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:564ab3d517e3d01c408c67f2e5247aad4019dcf1969982aba3974b4093279004"}, + {file = "yarl-1.20.1-cp312-cp312-win32.whl", hash = "sha256:daea0d313868da1cf2fac6b2d3a25c6e3a9e879483244be38c8e6a41f1d876a5"}, + {file = "yarl-1.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:48ea7d7f9be0487339828a4de0360d7ce0efc06524a48e1810f945c45b813698"}, + {file = "yarl-1.20.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0b5ff0fbb7c9f1b1b5ab53330acbfc5247893069e7716840c8e7d5bb7355038a"}, + {file = "yarl-1.20.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:14f326acd845c2b2e2eb38fb1346c94f7f3b01a4f5c788f8144f9b630bfff9a3"}, + {file = "yarl-1.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f60e4ad5db23f0b96e49c018596707c3ae89f5d0bd97f0ad3684bcbad899f1e7"}, + {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49bdd1b8e00ce57e68ba51916e4bb04461746e794e7c4d4bbc42ba2f18297691"}, + {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:66252d780b45189975abfed839616e8fd2dbacbdc262105ad7742c6ae58f3e31"}, + {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59174e7332f5d153d8f7452a102b103e2e74035ad085f404df2e40e663a22b28"}, + {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3968ec7d92a0c0f9ac34d5ecfd03869ec0cab0697c91a45db3fbbd95fe1b653"}, + {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1a4fbb50e14396ba3d375f68bfe02215d8e7bc3ec49da8341fe3157f59d2ff5"}, + {file = "yarl-1.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11a62c839c3a8eac2410e951301309426f368388ff2f33799052787035793b02"}, + {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:041eaa14f73ff5a8986b4388ac6bb43a77f2ea09bf1913df7a35d4646db69e53"}, + {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:377fae2fef158e8fd9d60b4c8751387b8d1fb121d3d0b8e9b0be07d1b41e83dc"}, + {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1c92f4390e407513f619d49319023664643d3339bd5e5a56a3bebe01bc67ec04"}, + {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d25ddcf954df1754ab0f86bb696af765c5bfaba39b74095f27eececa049ef9a4"}, + {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:909313577e9619dcff8c31a0ea2aa0a2a828341d92673015456b3ae492e7317b"}, + {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:793fd0580cb9664548c6b83c63b43c477212c0260891ddf86809e1c06c8b08f1"}, + {file = "yarl-1.20.1-cp313-cp313-win32.whl", hash = "sha256:468f6e40285de5a5b3c44981ca3a319a4b208ccc07d526b20b12aeedcfa654b7"}, + {file = "yarl-1.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:495b4ef2fea40596bfc0affe3837411d6aa3371abcf31aac0ccc4bdd64d4ef5c"}, + {file = "yarl-1.20.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f60233b98423aab21d249a30eb27c389c14929f47be8430efa7dbd91493a729d"}, + {file = "yarl-1.20.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6f3eff4cc3f03d650d8755c6eefc844edde99d641d0dcf4da3ab27141a5f8ddf"}, + {file = "yarl-1.20.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:69ff8439d8ba832d6bed88af2c2b3445977eba9a4588b787b32945871c2444e3"}, + {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cf34efa60eb81dd2645a2e13e00bb98b76c35ab5061a3989c7a70f78c85006d"}, + {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8e0fe9364ad0fddab2688ce72cb7a8e61ea42eff3c7caeeb83874a5d479c896c"}, + {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f64fbf81878ba914562c672024089e3401974a39767747691c65080a67b18c1"}, + {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6342d643bf9a1de97e512e45e4b9560a043347e779a173250824f8b254bd5ce"}, + {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56dac5f452ed25eef0f6e3c6a066c6ab68971d96a9fb441791cad0efba6140d3"}, + {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7d7f497126d65e2cad8dc5f97d34c27b19199b6414a40cb36b52f41b79014be"}, + {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:67e708dfb8e78d8a19169818eeb5c7a80717562de9051bf2413aca8e3696bf16"}, + {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:595c07bc79af2494365cc96ddeb772f76272364ef7c80fb892ef9d0649586513"}, + {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7bdd2f80f4a7df852ab9ab49484a4dee8030023aa536df41f2d922fd57bf023f"}, + {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c03bfebc4ae8d862f853a9757199677ab74ec25424d0ebd68a0027e9c639a390"}, + {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:344d1103e9c1523f32a5ed704d576172d2cabed3122ea90b1d4e11fe17c66458"}, + {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:88cab98aa4e13e1ade8c141daeedd300a4603b7132819c484841bb7af3edce9e"}, + {file = "yarl-1.20.1-cp313-cp313t-win32.whl", hash = "sha256:b121ff6a7cbd4abc28985b6028235491941b9fe8fe226e6fdc539c977ea1739d"}, + {file = "yarl-1.20.1-cp313-cp313t-win_amd64.whl", hash = "sha256:541d050a355bbbc27e55d906bc91cb6fe42f96c01413dd0f4ed5a5240513874f"}, + {file = "yarl-1.20.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e42ba79e2efb6845ebab49c7bf20306c4edf74a0b20fc6b2ccdd1a219d12fad3"}, + {file = "yarl-1.20.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:41493b9b7c312ac448b7f0a42a089dffe1d6e6e981a2d76205801a023ed26a2b"}, + {file = "yarl-1.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f5a5928ff5eb13408c62a968ac90d43f8322fd56d87008b8f9dabf3c0f6ee983"}, + {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30c41ad5d717b3961b2dd785593b67d386b73feca30522048d37298fee981805"}, + {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:59febc3969b0781682b469d4aca1a5cab7505a4f7b85acf6db01fa500fa3f6ba"}, + {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d2b6fb3622b7e5bf7a6e5b679a69326b4279e805ed1699d749739a61d242449e"}, + {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:749d73611db8d26a6281086f859ea7ec08f9c4c56cec864e52028c8b328db723"}, + {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9427925776096e664c39e131447aa20ec738bdd77c049c48ea5200db2237e000"}, + {file = "yarl-1.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff70f32aa316393eaf8222d518ce9118148eddb8a53073c2403863b41033eed5"}, + {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c7ddf7a09f38667aea38801da8b8d6bfe81df767d9dfc8c88eb45827b195cd1c"}, + {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:57edc88517d7fc62b174fcfb2e939fbc486a68315d648d7e74d07fac42cec240"}, + {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:dab096ce479d5894d62c26ff4f699ec9072269d514b4edd630a393223f45a0ee"}, + {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14a85f3bd2d7bb255be7183e5d7d6e70add151a98edf56a770d6140f5d5f4010"}, + {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2c89b5c792685dd9cd3fa9761c1b9f46fc240c2a3265483acc1565769996a3f8"}, + {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:69e9b141de5511021942a6866990aea6d111c9042235de90e08f94cf972ca03d"}, + {file = "yarl-1.20.1-cp39-cp39-win32.whl", hash = "sha256:b5f307337819cdfdbb40193cad84978a029f847b0a357fbe49f712063cfc4f06"}, + {file = "yarl-1.20.1-cp39-cp39-win_amd64.whl", hash = "sha256:eae7bfe2069f9c1c5b05fc7fe5d612e5bbc089a39309904ee8b829e322dcad00"}, + {file = "yarl-1.20.1-py3-none-any.whl", hash = "sha256:83b8eb083fe4683c6115795d9fc1cfaf2cbbefb19b3a1cb68f6527460f483a77"}, + {file = "yarl-1.20.1.tar.gz", hash = "sha256:d017a4997ee50c91fd5466cef416231bb82177b93b029906cefc542ce14c35ac"}, ] [package.dependencies] @@ -1788,15 +1794,15 @@ propcache = ">=0.2.1" [[package]] name = "zipp" -version = "3.21.0" +version = "3.23.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version < \"3.10\"" +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version == \"3.9\"" files = [ - {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, - {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, + {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, + {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, ] [package.extras] @@ -1804,7 +1810,7 @@ check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \" cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] [metadata] From 265a3ab86d1438132bc2359e8b2147b3b1d78d13 Mon Sep 17 00:00:00 2001 From: Ryan Ahearn Date: Mon, 23 Jun 2025 13:43:34 -0400 Subject: [PATCH 204/264] Properly parse proxy urls with credentials in them (#242) --- cloudfoundry_client/doppler/client.py | 15 ++++++++++----- .../doppler/websocket_envelope_reader.py | 5 ++++- cloudfoundry_client/rlpgateway/client.py | 12 +++--------- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/cloudfoundry_client/doppler/client.py b/cloudfoundry_client/doppler/client.py index d79e869..7f4e5ad 100644 --- a/cloudfoundry_client/doppler/client.py +++ b/cloudfoundry_client/doppler/client.py @@ -19,16 +19,20 @@ class DopplerClient(object): def __init__(self, doppler_endpoint: str, proxy: str, verify_ssl: bool, credentials_manager: CredentialManager): self.proxy_host = None self.proxy_port = None + self.proxy_auth = None self.ws_doppler_endpoint = doppler_endpoint self.http_doppler_endpoint = re.sub("^ws", "http", doppler_endpoint) self.verify_ssl = verify_ssl self.credentials_manager = credentials_manager if proxy is not None and len(proxy) > 0: - proxy_domain = urlparse(proxy).netloc - idx = proxy_domain.find(":") - if 0 < idx < len(proxy_domain) - 2: - self.proxy_host = proxy_domain[:idx] - self.proxy_port = int(proxy_domain[idx + 1 :]) + proxy_parsed = urlparse(proxy) + self.proxy_host = proxy_parsed.hostname + if proxy_parsed.port is not None: + self.proxy_port = proxy_parsed.port + else: + self.proxy_port = 443 if proxy_parsed.scheme == "https" else 80 + if proxy_parsed.username is not None and proxy_parsed.password is not None: + self.proxy_auth = (proxy_parsed.username, proxy_parsed.password) def recent_logs(self, app_guid: str) -> EnvelopeStream: url = "%s/apps/%s/recentlogs" % (self.http_doppler_endpoint, app_guid) @@ -46,6 +50,7 @@ def stream_logs(self, app_guid: str) -> EnvelopeStream: verify_ssl=self.verify_ssl, proxy_host=self.proxy_host, proxy_port=self.proxy_port, + proxy_auth=self.proxy_auth, ) as websocket: for message in websocket: yield DopplerClient._parse_envelope(message) diff --git a/cloudfoundry_client/doppler/websocket_envelope_reader.py b/cloudfoundry_client/doppler/websocket_envelope_reader.py index 46c279a..3bb513e 100644 --- a/cloudfoundry_client/doppler/websocket_envelope_reader.py +++ b/cloudfoundry_client/doppler/websocket_envelope_reader.py @@ -1,5 +1,5 @@ import ssl -from typing import Callable, Optional +from typing import Callable, Optional, Tuple import websocket @@ -12,6 +12,7 @@ def __init__( verify_ssl: bool = True, proxy_host: Optional[str] = None, proxy_port: Optional[int] = None, + proxy_auth: Optional[Tuple[str, str]] = None, ): if not verify_ssl: self._ws = websocket.WebSocket(sslopt=dict(cert_reqs=ssl.CERT_NONE)) @@ -20,6 +21,7 @@ def __init__( self._url = url self._proxy_host = proxy_host self._proxy_port = proxy_port + self._proxy_auth = proxy_auth self._access_token_provider = access_token_provider def connect(self): @@ -27,6 +29,7 @@ def connect(self): if self._proxy_host is not None and self._proxy_port is not None: kw_args["http_proxy_host"] = self._proxy_host kw_args["http_proxy_port"] = str(self._proxy_port) + kw_args["http_proxy_auth"] = self._proxy_auth self._ws.connect(self._url, **kw_args) def close(self): diff --git a/cloudfoundry_client/rlpgateway/client.py b/cloudfoundry_client/rlpgateway/client.py index 8954597..f71d199 100644 --- a/cloudfoundry_client/rlpgateway/client.py +++ b/cloudfoundry_client/rlpgateway/client.py @@ -1,5 +1,4 @@ import logging -from urllib.parse import urlparse import aiohttp @@ -15,18 +14,13 @@ class RLPGatewayClient(object): """ def __init__(self, rlp_gateway_endpoint, proxy, verify_ssl, credentials_manager): - self.proxy_host = None - self.proxy_port = None + self.proxy = None self.rlp_gateway_endpoint = rlp_gateway_endpoint self.verify_ssl = verify_ssl self.credentials_manager = credentials_manager if proxy is not None and len(proxy) > 0: - proxy_domain = urlparse(proxy).netloc - idx = proxy_domain.find(":") - if 0 < idx < len(proxy_domain) - 2: - self.proxy_host = proxy_domain[:idx] - self.proxy_port = int(proxy_domain[idx + 1 :]) + self.proxy = proxy async def stream_logs(self, app_guid, **kwargs): url = f"{self.rlp_gateway_endpoint}/v2/read" @@ -40,7 +34,7 @@ async def stream_logs(self, app_guid, **kwargs): headers.update(kwargs["headers"]) if "params" in kwargs: params.update(kwargs["params"]) - async with aiohttp.ClientSession(headers=headers) as session: + async with aiohttp.ClientSession(headers=headers, proxy=self.proxy) as session: async with session.get(url=url, params=params) as response: if response.status == 204: yield {} From a63c376eaec2e3dd7215e2d2243de1c1939d0a40 Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Mon, 23 Jun 2025 19:44:48 +0200 Subject: [PATCH 205/264] Prepare version 1.38.5 --- cloudfoundry_client/__init__.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudfoundry_client/__init__.py b/cloudfoundry_client/__init__.py index d9ce17c..e672190 100644 --- a/cloudfoundry_client/__init__.py +++ b/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.38.4" +__version__ = "1.38.5" diff --git a/pyproject.toml b/pyproject.toml index f82eb7a..f3a242c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ exclude = ''' [tool.poetry] name = "cloudfoundry_client" -version = "1.38.4" +version = "1.38.5" description = "A client library for CloudFoundry" authors = ["Benjamin Einaudi "] readme = "README.rst" From a47faf14e843de809d32c95506d719b568f51cbc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 5 Jul 2025 12:09:41 +0200 Subject: [PATCH 206/264] Bump pytest from 8.3.5 to 8.4.1 (#246) Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.3.5 to 8.4.1. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/8.3.5...8.4.1) --- updated-dependencies: - dependency-name: pytest dependency-version: 8.4.1 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 27 ++++++++++++++------------- pyproject.toml | 2 +- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/poetry.lock b/poetry.lock index ebf4f62..578b5d8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -711,7 +711,7 @@ description = "Read metadata from Python packages" optional = false python-versions = ">=3.9" groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version == \"3.9\"" +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version < \"3.10\"" files = [ {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"}, {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"}, @@ -1349,26 +1349,27 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pytest" -version = "8.3.5" +version = "8.4.1" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, - {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, + {file = "pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7"}, + {file = "pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"}, ] [package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -iniconfig = "*" -packaging = "*" +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""} +iniconfig = ">=1" +packaging = ">=20" pluggy = ">=1.5,<2" +pygments = ">=2.7.2" tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] -dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] [[package]] name = "pywin32-ctypes" @@ -1799,7 +1800,7 @@ description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version == \"3.9\"" +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version < \"3.10\"" files = [ {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, @@ -1816,4 +1817,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9" -content-hash = "5cb2cca4213b415b57ffc5cb3214d425952521e0ce5dde7f07efc57e21e0b831" +content-hash = "127ff227fe4de6ea1f7bd8f9b1bc5ed6025a5468758caf907b93399df2de728b" diff --git a/pyproject.toml b/pyproject.toml index f3a242c..4734382 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ polling2= "0.5.0" [tool.poetry.group.dev.dependencies] black= "25.1.0" flake8= "7.2.0" -pytest = ">=8.2.2,<8.4.0" +pytest = ">=8.2.2,<8.5.0" twine = ">=6.0,<6.2" [tool.poetry.scripts] From 822f185f01bcd1ebf34d095b8d5d034acbc02951 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 5 Jul 2025 12:15:30 +0200 Subject: [PATCH 207/264] Bump flake8 from 7.2.0 to 7.3.0 (#245) Bumps [flake8](https://github.com/pycqa/flake8) from 7.2.0 to 7.3.0. - [Commits](https://github.com/pycqa/flake8/compare/7.2.0...7.3.0) --- updated-dependencies: - dependency-name: flake8 dependency-version: 7.3.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 24 ++++++++++++------------ pyproject.toml | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/poetry.lock b/poetry.lock index 578b5d8..ac0bf64 100644 --- a/poetry.lock +++ b/poetry.lock @@ -540,20 +540,20 @@ test = ["pytest (>=6)"] [[package]] name = "flake8" -version = "7.2.0" +version = "7.3.0" description = "the modular source code checker: pep8 pyflakes and co" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "flake8-7.2.0-py2.py3-none-any.whl", hash = "sha256:93b92ba5bdb60754a6da14fa3b93a9361fd00a59632ada61fd7b130436c40343"}, - {file = "flake8-7.2.0.tar.gz", hash = "sha256:fa558ae3f6f7dbf2b4f22663e5343b6b6023620461f8d4ff2019ef4b5ee70426"}, + {file = "flake8-7.3.0-py2.py3-none-any.whl", hash = "sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e"}, + {file = "flake8-7.3.0.tar.gz", hash = "sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872"}, ] [package.dependencies] mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.13.0,<2.14.0" -pyflakes = ">=3.3.0,<3.4.0" +pycodestyle = ">=2.14.0,<2.15.0" +pyflakes = ">=3.4.0,<3.5.0" [[package]] name = "frozenlist" @@ -1297,14 +1297,14 @@ files = [ [[package]] name = "pycodestyle" -version = "2.13.0" +version = "2.14.0" description = "Python style guide checker" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "pycodestyle-2.13.0-py2.py3-none-any.whl", hash = "sha256:35863c5974a271c7a726ed228a14a4f6daf49df369d8c50cd9a6f58a5e143ba9"}, - {file = "pycodestyle-2.13.0.tar.gz", hash = "sha256:c8415bf09abe81d9c7f872502a6eee881fbe85d8763dd5b9924bb0a01d67efae"}, + {file = "pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d"}, + {file = "pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783"}, ] [[package]] @@ -1322,14 +1322,14 @@ files = [ [[package]] name = "pyflakes" -version = "3.3.2" +version = "3.4.0" description = "passive checker of Python programs" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "pyflakes-3.3.2-py2.py3-none-any.whl", hash = "sha256:5039c8339cbb1944045f4ee5466908906180f13cc99cc9949348d10f82a5c32a"}, - {file = "pyflakes-3.3.2.tar.gz", hash = "sha256:6dfd61d87b97fba5dcfaaf781171ac16be16453be6d816147989e7f6e6a9576b"}, + {file = "pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f"}, + {file = "pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58"}, ] [[package]] @@ -1817,4 +1817,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9" -content-hash = "127ff227fe4de6ea1f7bd8f9b1bc5ed6025a5468758caf907b93399df2de728b" +content-hash = "db664a7a897340112cff748a823bc9b234ea51369783a5be2597d1216f0b0b4b" diff --git a/pyproject.toml b/pyproject.toml index 4734382..141e793 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,7 @@ polling2= "0.5.0" [tool.poetry.group.dev.dependencies] black= "25.1.0" -flake8= "7.2.0" +flake8= "7.3.0" pytest = ">=8.2.2,<8.5.0" twine = ">=6.0,<6.2" From 16a181da948ee305782531c673aca7a5c131bcc0 Mon Sep 17 00:00:00 2001 From: David Duarte <102606398+daviddmd@users.noreply.github.com> Date: Sat, 5 Jul 2025 20:56:12 +0200 Subject: [PATCH 208/264] feat(apps): Add Generate App Manifest functionality (#244) * fix(networking): Update entities _get_url_filtered function fix(networking): Update _append_encoded_parameter function to append the parameters with the %s=%s format, in order to be compliant with the Policy Server API interface * feat(apps): Add function for the /v3/apps/:guid/manifest endpoint * test: Add test for the v3 apps get_manifest function * test: Fix formatting of statements in test_get_manifest function --- cloudfoundry_client/networking/entities.py | 4 +-- cloudfoundry_client/v3/apps.py | 3 ++ .../v3/apps/GET_{id}_manifest_response.yml | 28 +++++++++++++++++++ tests/v3/test_apps.py | 28 +++++++++++++++++++ 4 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 tests/fixtures/v3/apps/GET_{id}_manifest_response.yml diff --git a/cloudfoundry_client/networking/entities.py b/cloudfoundry_client/networking/entities.py index da393d6..07d34e7 100644 --- a/cloudfoundry_client/networking/entities.py +++ b/cloudfoundry_client/networking/entities.py @@ -116,9 +116,9 @@ def _append_encoded_parameter(parameters: List[str], args: Tuple[str, Any]) -> L for value in value_list: parameters.append("%s=%s" % (parameter_name, str(value))) elif isinstance(parameter_value, (list, tuple)): - parameters.append("q=%s" % quote("%s IN %s" % (parameter_name, ",".join(parameter_value)))) + parameters.append("%s=%s" % (parameter_name, quote(",".join(parameter_value)))) else: - parameters.append("q=%s" % quote("%s:%s" % (parameter_name, str(parameter_value)))) + parameters.append("%s=%s" % (parameter_name, quote(str(parameter_value)))) return parameters if len(kwargs) > 0: diff --git a/cloudfoundry_client/v3/apps.py b/cloudfoundry_client/v3/apps.py index 1453575..4382ea2 100644 --- a/cloudfoundry_client/v3/apps.py +++ b/cloudfoundry_client/v3/apps.py @@ -24,3 +24,6 @@ def get_env(self, application_guid: str) -> JsonObject: def get_routes(self, application_guid: str) -> JsonObject: return super(AppManager, self)._get("%s%s/%s/routes" % (self.target_endpoint, self.entity_uri, application_guid)) + + def get_manifest(self, application_guid: str) -> str: + return self.client.get(url="%s%s/%s/manifest" % (self.target_endpoint, self.entity_uri, application_guid)).text diff --git a/tests/fixtures/v3/apps/GET_{id}_manifest_response.yml b/tests/fixtures/v3/apps/GET_{id}_manifest_response.yml new file mode 100644 index 0000000..f263616 --- /dev/null +++ b/tests/fixtures/v3/apps/GET_{id}_manifest_response.yml @@ -0,0 +1,28 @@ +--- +applications: + - name: my-app + stack: cflinuxfs4 + features: + ssh: true + revisions: true + service-binding-k8s: false + file-based-vcap-services: false + services: + - my-service + routes: + - route: my-app.example.com + protocol: http1 + processes: + - type: web + instances: 2 + memory: 512M + log-rate-limit-per-second: 1KB + disk_quota: 1024M + health-check-type: http + health-check-http-endpoint: /healthy + health-check-invocation-timeout: 10 + health-check-interval: 5 + readiness-health-check-type: http + readiness-health-check-http-endpoint: /ready + readiness-health-check-invocation-timeout: 20 + readiness-health-check-interval: 5 \ No newline at end of file diff --git a/tests/v3/test_apps.py b/tests/v3/test_apps.py index c601170..8aeb0cf 100644 --- a/tests/v3/test_apps.py +++ b/tests/v3/test_apps.py @@ -1,5 +1,7 @@ import unittest +import yaml from http import HTTPStatus +from typing import Optional, List, Union from abstract_test_case import AbstractTestCase from cloudfoundry_client.common_objects import JsonObject @@ -132,3 +134,29 @@ def test_list_include_space(self): self.assertIsInstance(all_spaces[0], Entity) self.assertEqual(all_spaces[1]["name"], "my_space") self.assertIsInstance(all_spaces[1], Entity) + + def test_get_manifest(self): + self.client.get.return_value = self.mock_response( + "/v3/apps/app_id/manifest", HTTPStatus.OK, {"Content-Type": "application/x-yaml"}, "v3", "apps", + "GET_{id}_manifest_response.yml" + ) + manifest_response: str = self.client.v3.apps.get_manifest("app_id") + self.assertIsInstance(manifest_response, str) + manifest: dict = yaml.safe_load(manifest_response) + applications: Optional[list[dict]] = manifest.get("applications") + self.assertIsInstance(applications, list) + self.assertEqual(len(applications), 1) + application: dict = applications[0] + self.assertEqual(application.get("name"), "my-app") + self.assertEqual(application.get("stack"), "cflinuxfs4") + application_services: Optional[list[str]] = application.get("services") + self.assertIsInstance(application_services, list) + self.assertEqual(len(application_services), 1) + self.assertEqual(application_services[0], "my-service") + application_routes: Optional[List[Union[dict, str]]] = application.get("routes") + self.assertIsInstance(application_routes, list) + self.assertEqual(len(application_routes), 1) + application_route: dict = application_routes[0] + self.assertIsInstance(application_route, dict) + self.assertEqual(application_route.get("route"), "my-app.example.com") + self.assertEqual(application_route.get("protocol"), "http1") From 49089eef965ceb95babe1053cdf5b503a0c5f656 Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Sun, 6 Jul 2025 11:27:01 +0200 Subject: [PATCH 209/264] prepare 1.38.6 version --- cloudfoundry_client/__init__.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudfoundry_client/__init__.py b/cloudfoundry_client/__init__.py index e672190..f468d98 100644 --- a/cloudfoundry_client/__init__.py +++ b/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.38.5" +__version__ = "1.38.6" diff --git a/pyproject.toml b/pyproject.toml index 141e793..c7e5400 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ exclude = ''' [tool.poetry] name = "cloudfoundry_client" -version = "1.38.5" +version = "1.38.6" description = "A client library for CloudFoundry" authors = ["Benjamin Einaudi "] readme = "README.rst" From 141452629921df6a52c0690c092552481f921f69 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Jul 2025 10:29:34 +0200 Subject: [PATCH 210/264] Bump aiohttp from 3.12.13 to 3.12.14 (#249) --- updated-dependencies: - dependency-name: aiohttp dependency-version: 3.12.14 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 185 ++++++++++++++++++++++++++-------------------------- 1 file changed, 93 insertions(+), 92 deletions(-) diff --git a/poetry.lock b/poetry.lock index ac0bf64..53e2b59 100644 --- a/poetry.lock +++ b/poetry.lock @@ -14,103 +14,103 @@ files = [ [[package]] name = "aiohttp" -version = "3.12.13" +version = "3.12.14" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5421af8f22a98f640261ee48aae3a37f0c41371e99412d55eaf2f8a46d5dad29"}, - {file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fcda86f6cb318ba36ed8f1396a6a4a3fd8f856f84d426584392083d10da4de0"}, - {file = "aiohttp-3.12.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cd71c9fb92aceb5a23c4c39d8ecc80389c178eba9feab77f19274843eb9412d"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34ebf1aca12845066c963016655dac897651e1544f22a34c9b461ac3b4b1d3aa"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:893a4639694c5b7edd4bdd8141be296042b6806e27cc1d794e585c43010cc294"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:663d8ee3ffb3494502ebcccb49078faddbb84c1d870f9c1dd5a29e85d1f747ce"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0f8f6a85a0006ae2709aa4ce05749ba2cdcb4b43d6c21a16c8517c16593aabe"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1582745eb63df267c92d8b61ca655a0ce62105ef62542c00a74590f306be8cb5"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d59227776ee2aa64226f7e086638baa645f4b044f2947dbf85c76ab11dcba073"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06b07c418bde1c8e737d8fa67741072bd3f5b0fb66cf8c0655172188c17e5fa6"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:9445c1842680efac0f81d272fd8db7163acfcc2b1436e3f420f4c9a9c5a50795"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:09c4767af0b0b98c724f5d47f2bf33395c8986995b0a9dab0575ca81a554a8c0"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f3854fbde7a465318ad8d3fc5bef8f059e6d0a87e71a0d3360bb56c0bf87b18a"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2332b4c361c05ecd381edb99e2a33733f3db906739a83a483974b3df70a51b40"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1561db63fa1b658cd94325d303933553ea7d89ae09ff21cc3bcd41b8521fbbb6"}, - {file = "aiohttp-3.12.13-cp310-cp310-win32.whl", hash = "sha256:a0be857f0b35177ba09d7c472825d1b711d11c6d0e8a2052804e3b93166de1ad"}, - {file = "aiohttp-3.12.13-cp310-cp310-win_amd64.whl", hash = "sha256:fcc30ad4fb5cb41a33953292d45f54ef4066746d625992aeac33b8c681173178"}, - {file = "aiohttp-3.12.13-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c229b1437aa2576b99384e4be668af1db84b31a45305d02f61f5497cfa6f60c"}, - {file = "aiohttp-3.12.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:04076d8c63471e51e3689c93940775dc3d12d855c0c80d18ac5a1c68f0904358"}, - {file = "aiohttp-3.12.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:55683615813ce3601640cfaa1041174dc956d28ba0511c8cbd75273eb0587014"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:921bc91e602d7506d37643e77819cb0b840d4ebb5f8d6408423af3d3bf79a7b7"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e72d17fe0974ddeae8ed86db297e23dba39c7ac36d84acdbb53df2e18505a013"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0653d15587909a52e024a261943cf1c5bdc69acb71f411b0dd5966d065a51a47"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a77b48997c66722c65e157c06c74332cdf9c7ad00494b85ec43f324e5c5a9b9a"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6946bae55fd36cfb8e4092c921075cde029c71c7cb571d72f1079d1e4e013bc"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f95db8c8b219bcf294a53742c7bda49b80ceb9d577c8e7aa075612b7f39ffb7"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:03d5eb3cfb4949ab4c74822fb3326cd9655c2b9fe22e4257e2100d44215b2e2b"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6383dd0ffa15515283c26cbf41ac8e6705aab54b4cbb77bdb8935a713a89bee9"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6548a411bc8219b45ba2577716493aa63b12803d1e5dc70508c539d0db8dbf5a"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:81b0fcbfe59a4ca41dc8f635c2a4a71e63f75168cc91026c61be665945739e2d"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6a83797a0174e7995e5edce9dcecc517c642eb43bc3cba296d4512edf346eee2"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5734d8469a5633a4e9ffdf9983ff7cdb512524645c7a3d4bc8a3de45b935ac3"}, - {file = "aiohttp-3.12.13-cp311-cp311-win32.whl", hash = "sha256:fef8d50dfa482925bb6b4c208b40d8e9fa54cecba923dc65b825a72eed9a5dbd"}, - {file = "aiohttp-3.12.13-cp311-cp311-win_amd64.whl", hash = "sha256:9a27da9c3b5ed9d04c36ad2df65b38a96a37e9cfba6f1381b842d05d98e6afe9"}, - {file = "aiohttp-3.12.13-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0aa580cf80558557285b49452151b9c69f2fa3ad94c5c9e76e684719a8791b73"}, - {file = "aiohttp-3.12.13-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b103a7e414b57e6939cc4dece8e282cfb22043efd0c7298044f6594cf83ab347"}, - {file = "aiohttp-3.12.13-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78f64e748e9e741d2eccff9597d09fb3cd962210e5b5716047cbb646dc8fe06f"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c955989bf4c696d2ededc6b0ccb85a73623ae6e112439398935362bacfaaf6"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d640191016763fab76072c87d8854a19e8e65d7a6fcfcbf017926bdbbb30a7e5"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4dc507481266b410dede95dd9f26c8d6f5a14315372cc48a6e43eac652237d9b"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8a94daa873465d518db073bd95d75f14302e0208a08e8c942b2f3f1c07288a75"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f52420cde4ce0bb9425a375d95577fe082cb5721ecb61da3049b55189e4e6"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f7df1f620ec40f1a7fbcb99ea17d7326ea6996715e78f71a1c9a021e31b96b8"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3062d4ad53b36e17796dce1c0d6da0ad27a015c321e663657ba1cc7659cfc710"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:8605e22d2a86b8e51ffb5253d9045ea73683d92d47c0b1438e11a359bdb94462"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:54fbbe6beafc2820de71ece2198458a711e224e116efefa01b7969f3e2b3ddae"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:050bd277dfc3768b606fd4eae79dd58ceda67d8b0b3c565656a89ae34525d15e"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2637a60910b58f50f22379b6797466c3aa6ae28a6ab6404e09175ce4955b4e6a"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e986067357550d1aaa21cfe9897fa19e680110551518a5a7cf44e6c5638cb8b5"}, - {file = "aiohttp-3.12.13-cp312-cp312-win32.whl", hash = "sha256:ac941a80aeea2aaae2875c9500861a3ba356f9ff17b9cb2dbfb5cbf91baaf5bf"}, - {file = "aiohttp-3.12.13-cp312-cp312-win_amd64.whl", hash = "sha256:671f41e6146a749b6c81cb7fd07f5a8356d46febdaaaf07b0e774ff04830461e"}, - {file = "aiohttp-3.12.13-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d4a18e61f271127465bdb0e8ff36e8f02ac4a32a80d8927aa52371e93cd87938"}, - {file = "aiohttp-3.12.13-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:532542cb48691179455fab429cdb0d558b5e5290b033b87478f2aa6af5d20ace"}, - {file = "aiohttp-3.12.13-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d7eea18b52f23c050ae9db5d01f3d264ab08f09e7356d6f68e3f3ac2de9dfabb"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad7c8e5c25f2a26842a7c239de3f7b6bfb92304593ef997c04ac49fb703ff4d7"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6af355b483e3fe9d7336d84539fef460120c2f6e50e06c658fe2907c69262d6b"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a95cf9f097498f35c88e3609f55bb47b28a5ef67f6888f4390b3d73e2bac6177"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8ed8c38a1c584fe99a475a8f60eefc0b682ea413a84c6ce769bb19a7ff1c5ef"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a0b9170d5d800126b5bc89d3053a2363406d6e327afb6afaeda2d19ee8bb103"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:372feeace612ef8eb41f05ae014a92121a512bd5067db8f25101dd88a8db11da"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a946d3702f7965d81f7af7ea8fb03bb33fe53d311df48a46eeca17e9e0beed2d"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a0c4725fae86555bbb1d4082129e21de7264f4ab14baf735278c974785cd2041"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9b28ea2f708234f0a5c44eb6c7d9eb63a148ce3252ba0140d050b091b6e842d1"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d4f5becd2a5791829f79608c6f3dc745388162376f310eb9c142c985f9441cc1"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:60f2ce6b944e97649051d5f5cc0f439360690b73909230e107fd45a359d3e911"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:69fc1909857401b67bf599c793f2183fbc4804717388b0b888f27f9929aa41f3"}, - {file = "aiohttp-3.12.13-cp313-cp313-win32.whl", hash = "sha256:7d7e68787a2046b0e44ba5587aa723ce05d711e3a3665b6b7545328ac8e3c0dd"}, - {file = "aiohttp-3.12.13-cp313-cp313-win_amd64.whl", hash = "sha256:5a178390ca90419bfd41419a809688c368e63c86bd725e1186dd97f6b89c2706"}, - {file = "aiohttp-3.12.13-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:36f6c973e003dc9b0bb4e8492a643641ea8ef0e97ff7aaa5c0f53d68839357b4"}, - {file = "aiohttp-3.12.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6cbfc73179bd67c229eb171e2e3745d2afd5c711ccd1e40a68b90427f282eab1"}, - {file = "aiohttp-3.12.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1e8b27b2d414f7e3205aa23bb4a692e935ef877e3a71f40d1884f6e04fd7fa74"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eabded0c2b2ef56243289112c48556c395d70150ce4220d9008e6b4b3dd15690"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:003038e83f1a3ff97409999995ec02fe3008a1d675478949643281141f54751d"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b6f46613031dbc92bdcaad9c4c22c7209236ec501f9c0c5f5f0b6a689bf50f3"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c332c6bb04650d59fb94ed96491f43812549a3ba6e7a16a218e612f99f04145e"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fea41a2c931fb582cb15dc86a3037329e7b941df52b487a9f8b5aa960153cbd"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:846104f45d18fb390efd9b422b27d8f3cf8853f1218c537f36e71a385758c896"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d6c85ac7dd350f8da2520bac8205ce99df4435b399fa7f4dc4a70407073e390"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5a1ecce0ed281bec7da8550da052a6b89552db14d0a0a45554156f085a912f48"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:5304d74867028cca8f64f1cc1215eb365388033c5a691ea7aa6b0dc47412f495"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:64d1f24ee95a2d1e094a4cd7a9b7d34d08db1bbcb8aa9fb717046b0a884ac294"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:119c79922a7001ca6a9e253228eb39b793ea994fd2eccb79481c64b5f9d2a055"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bb18f00396d22e2f10cd8825d671d9f9a3ba968d708a559c02a627536b36d91c"}, - {file = "aiohttp-3.12.13-cp39-cp39-win32.whl", hash = "sha256:0022de47ef63fd06b065d430ac79c6b0bd24cdae7feaf0e8c6bac23b805a23a8"}, - {file = "aiohttp-3.12.13-cp39-cp39-win_amd64.whl", hash = "sha256:29e08111ccf81b2734ae03f1ad1cb03b9615e7d8f616764f22f71209c094f122"}, - {file = "aiohttp-3.12.13.tar.gz", hash = "sha256:47e2da578528264a12e4e3dd8dd72a7289e5f812758fe086473fab037a10fcce"}, + {file = "aiohttp-3.12.14-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:906d5075b5ba0dd1c66fcaaf60eb09926a9fef3ca92d912d2a0bbdbecf8b1248"}, + {file = "aiohttp-3.12.14-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c875bf6fc2fd1a572aba0e02ef4e7a63694778c5646cdbda346ee24e630d30fb"}, + {file = "aiohttp-3.12.14-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fbb284d15c6a45fab030740049d03c0ecd60edad9cd23b211d7e11d3be8d56fd"}, + {file = "aiohttp-3.12.14-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38e360381e02e1a05d36b223ecab7bc4a6e7b5ab15760022dc92589ee1d4238c"}, + {file = "aiohttp-3.12.14-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:aaf90137b5e5d84a53632ad95ebee5c9e3e7468f0aab92ba3f608adcb914fa95"}, + {file = "aiohttp-3.12.14-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e532a25e4a0a2685fa295a31acf65e027fbe2bea7a4b02cdfbbba8a064577663"}, + {file = "aiohttp-3.12.14-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eab9762c4d1b08ae04a6c77474e6136da722e34fdc0e6d6eab5ee93ac29f35d1"}, + {file = "aiohttp-3.12.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abe53c3812b2899889a7fca763cdfaeee725f5be68ea89905e4275476ffd7e61"}, + {file = "aiohttp-3.12.14-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5760909b7080aa2ec1d320baee90d03b21745573780a072b66ce633eb77a8656"}, + {file = "aiohttp-3.12.14-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:02fcd3f69051467bbaa7f84d7ec3267478c7df18d68b2e28279116e29d18d4f3"}, + {file = "aiohttp-3.12.14-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4dcd1172cd6794884c33e504d3da3c35648b8be9bfa946942d353b939d5f1288"}, + {file = "aiohttp-3.12.14-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:224d0da41355b942b43ad08101b1b41ce633a654128ee07e36d75133443adcda"}, + {file = "aiohttp-3.12.14-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e387668724f4d734e865c1776d841ed75b300ee61059aca0b05bce67061dcacc"}, + {file = "aiohttp-3.12.14-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:dec9cde5b5a24171e0b0a4ca064b1414950904053fb77c707efd876a2da525d8"}, + {file = "aiohttp-3.12.14-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bbad68a2af4877cc103cd94af9160e45676fc6f0c14abb88e6e092b945c2c8e3"}, + {file = "aiohttp-3.12.14-cp310-cp310-win32.whl", hash = "sha256:ee580cb7c00bd857b3039ebca03c4448e84700dc1322f860cf7a500a6f62630c"}, + {file = "aiohttp-3.12.14-cp310-cp310-win_amd64.whl", hash = "sha256:cf4f05b8cea571e2ccc3ca744e35ead24992d90a72ca2cf7ab7a2efbac6716db"}, + {file = "aiohttp-3.12.14-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f4552ff7b18bcec18b60a90c6982049cdb9dac1dba48cf00b97934a06ce2e597"}, + {file = "aiohttp-3.12.14-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8283f42181ff6ccbcf25acaae4e8ab2ff7e92b3ca4a4ced73b2c12d8cd971393"}, + {file = "aiohttp-3.12.14-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:040afa180ea514495aaff7ad34ec3d27826eaa5d19812730fe9e529b04bb2179"}, + {file = "aiohttp-3.12.14-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b413c12f14c1149f0ffd890f4141a7471ba4b41234fe4fd4a0ff82b1dc299dbb"}, + {file = "aiohttp-3.12.14-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1d6f607ce2e1a93315414e3d448b831238f1874b9968e1195b06efaa5c87e245"}, + {file = "aiohttp-3.12.14-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:565e70d03e924333004ed101599902bba09ebb14843c8ea39d657f037115201b"}, + {file = "aiohttp-3.12.14-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4699979560728b168d5ab63c668a093c9570af2c7a78ea24ca5212c6cdc2b641"}, + {file = "aiohttp-3.12.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad5fdf6af93ec6c99bf800eba3af9a43d8bfd66dce920ac905c817ef4a712afe"}, + {file = "aiohttp-3.12.14-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ac76627c0b7ee0e80e871bde0d376a057916cb008a8f3ffc889570a838f5cc7"}, + {file = "aiohttp-3.12.14-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:798204af1180885651b77bf03adc903743a86a39c7392c472891649610844635"}, + {file = "aiohttp-3.12.14-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:4f1205f97de92c37dd71cf2d5bcfb65fdaed3c255d246172cce729a8d849b4da"}, + {file = "aiohttp-3.12.14-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:76ae6f1dd041f85065d9df77c6bc9c9703da9b5c018479d20262acc3df97d419"}, + {file = "aiohttp-3.12.14-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a194ace7bc43ce765338ca2dfb5661489317db216ea7ea700b0332878b392cab"}, + {file = "aiohttp-3.12.14-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:16260e8e03744a6fe3fcb05259eeab8e08342c4c33decf96a9dad9f1187275d0"}, + {file = "aiohttp-3.12.14-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8c779e5ebbf0e2e15334ea404fcce54009dc069210164a244d2eac8352a44b28"}, + {file = "aiohttp-3.12.14-cp311-cp311-win32.whl", hash = "sha256:a289f50bf1bd5be227376c067927f78079a7bdeccf8daa6a9e65c38bae14324b"}, + {file = "aiohttp-3.12.14-cp311-cp311-win_amd64.whl", hash = "sha256:0b8a69acaf06b17e9c54151a6c956339cf46db4ff72b3ac28516d0f7068f4ced"}, + {file = "aiohttp-3.12.14-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a0ecbb32fc3e69bc25efcda7d28d38e987d007096cbbeed04f14a6662d0eee22"}, + {file = "aiohttp-3.12.14-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0400f0ca9bb3e0b02f6466421f253797f6384e9845820c8b05e976398ac1d81a"}, + {file = "aiohttp-3.12.14-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a56809fed4c8a830b5cae18454b7464e1529dbf66f71c4772e3cfa9cbec0a1ff"}, + {file = "aiohttp-3.12.14-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27f2e373276e4755691a963e5d11756d093e346119f0627c2d6518208483fb6d"}, + {file = "aiohttp-3.12.14-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ca39e433630e9a16281125ef57ece6817afd1d54c9f1bf32e901f38f16035869"}, + {file = "aiohttp-3.12.14-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c748b3f8b14c77720132b2510a7d9907a03c20ba80f469e58d5dfd90c079a1c"}, + {file = "aiohttp-3.12.14-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0a568abe1b15ce69d4cc37e23020720423f0728e3cb1f9bcd3f53420ec3bfe7"}, + {file = "aiohttp-3.12.14-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9888e60c2c54eaf56704b17feb558c7ed6b7439bca1e07d4818ab878f2083660"}, + {file = "aiohttp-3.12.14-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3006a1dc579b9156de01e7916d38c63dc1ea0679b14627a37edf6151bc530088"}, + {file = "aiohttp-3.12.14-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aa8ec5c15ab80e5501a26719eb48a55f3c567da45c6ea5bb78c52c036b2655c7"}, + {file = "aiohttp-3.12.14-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:39b94e50959aa07844c7fe2206b9f75d63cc3ad1c648aaa755aa257f6f2498a9"}, + {file = "aiohttp-3.12.14-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:04c11907492f416dad9885d503fbfc5dcb6768d90cad8639a771922d584609d3"}, + {file = "aiohttp-3.12.14-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:88167bd9ab69bb46cee91bd9761db6dfd45b6e76a0438c7e884c3f8160ff21eb"}, + {file = "aiohttp-3.12.14-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:791504763f25e8f9f251e4688195e8b455f8820274320204f7eafc467e609425"}, + {file = "aiohttp-3.12.14-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2785b112346e435dd3a1a67f67713a3fe692d288542f1347ad255683f066d8e0"}, + {file = "aiohttp-3.12.14-cp312-cp312-win32.whl", hash = "sha256:15f5f4792c9c999a31d8decf444e79fcfd98497bf98e94284bf390a7bb8c1729"}, + {file = "aiohttp-3.12.14-cp312-cp312-win_amd64.whl", hash = "sha256:3b66e1a182879f579b105a80d5c4bd448b91a57e8933564bf41665064796a338"}, + {file = "aiohttp-3.12.14-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3143a7893d94dc82bc409f7308bc10d60285a3cd831a68faf1aa0836c5c3c767"}, + {file = "aiohttp-3.12.14-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3d62ac3d506cef54b355bd34c2a7c230eb693880001dfcda0bf88b38f5d7af7e"}, + {file = "aiohttp-3.12.14-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:48e43e075c6a438937c4de48ec30fa8ad8e6dfef122a038847456bfe7b947b63"}, + {file = "aiohttp-3.12.14-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:077b4488411a9724cecc436cbc8c133e0d61e694995b8de51aaf351c7578949d"}, + {file = "aiohttp-3.12.14-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d8c35632575653f297dcbc9546305b2c1133391089ab925a6a3706dfa775ccab"}, + {file = "aiohttp-3.12.14-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b8ce87963f0035c6834b28f061df90cf525ff7c9b6283a8ac23acee6502afd4"}, + {file = "aiohttp-3.12.14-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0a2cf66e32a2563bb0766eb24eae7e9a269ac0dc48db0aae90b575dc9583026"}, + {file = "aiohttp-3.12.14-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdea089caf6d5cde975084a884c72d901e36ef9c2fd972c9f51efbbc64e96fbd"}, + {file = "aiohttp-3.12.14-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a7865f27db67d49e81d463da64a59365ebd6b826e0e4847aa111056dcb9dc88"}, + {file = "aiohttp-3.12.14-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0ab5b38a6a39781d77713ad930cb5e7feea6f253de656a5f9f281a8f5931b086"}, + {file = "aiohttp-3.12.14-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:9b3b15acee5c17e8848d90a4ebc27853f37077ba6aec4d8cb4dbbea56d156933"}, + {file = "aiohttp-3.12.14-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e4c972b0bdaac167c1e53e16a16101b17c6d0ed7eac178e653a07b9f7fad7151"}, + {file = "aiohttp-3.12.14-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7442488b0039257a3bdbc55f7209587911f143fca11df9869578db6c26feeeb8"}, + {file = "aiohttp-3.12.14-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f68d3067eecb64c5e9bab4a26aa11bd676f4c70eea9ef6536b0a4e490639add3"}, + {file = "aiohttp-3.12.14-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f88d3704c8b3d598a08ad17d06006cb1ca52a1182291f04979e305c8be6c9758"}, + {file = "aiohttp-3.12.14-cp313-cp313-win32.whl", hash = "sha256:a3c99ab19c7bf375c4ae3debd91ca5d394b98b6089a03231d4c580ef3c2ae4c5"}, + {file = "aiohttp-3.12.14-cp313-cp313-win_amd64.whl", hash = "sha256:3f8aad695e12edc9d571f878c62bedc91adf30c760c8632f09663e5f564f4baa"}, + {file = "aiohttp-3.12.14-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b8cc6b05e94d837bcd71c6531e2344e1ff0fb87abe4ad78a9261d67ef5d83eae"}, + {file = "aiohttp-3.12.14-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1dcb015ac6a3b8facd3677597edd5ff39d11d937456702f0bb2b762e390a21b"}, + {file = "aiohttp-3.12.14-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3779ed96105cd70ee5e85ca4f457adbce3d9ff33ec3d0ebcdf6c5727f26b21b3"}, + {file = "aiohttp-3.12.14-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:717a0680729b4ebd7569c1dcd718c46b09b360745fd8eb12317abc74b14d14d0"}, + {file = "aiohttp-3.12.14-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b5dd3a2ef7c7e968dbbac8f5574ebeac4d2b813b247e8cec28174a2ba3627170"}, + {file = "aiohttp-3.12.14-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4710f77598c0092239bc12c1fcc278a444e16c7032d91babf5abbf7166463f7b"}, + {file = "aiohttp-3.12.14-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f3e9f75ae842a6c22a195d4a127263dbf87cbab729829e0bd7857fb1672400b2"}, + {file = "aiohttp-3.12.14-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f9c8d55d6802086edd188e3a7d85a77787e50d56ce3eb4757a3205fa4657922"}, + {file = "aiohttp-3.12.14-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:79b29053ff3ad307880d94562cca80693c62062a098a5776ea8ef5ef4b28d140"}, + {file = "aiohttp-3.12.14-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:23e1332fff36bebd3183db0c7a547a1da9d3b4091509f6d818e098855f2f27d3"}, + {file = "aiohttp-3.12.14-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:a564188ce831fd110ea76bcc97085dd6c625b427db3f1dbb14ca4baa1447dcbc"}, + {file = "aiohttp-3.12.14-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:a7a1b4302f70bb3ec40ca86de82def532c97a80db49cac6a6700af0de41af5ee"}, + {file = "aiohttp-3.12.14-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:1b07ccef62950a2519f9bfc1e5b294de5dd84329f444ca0b329605ea787a3de5"}, + {file = "aiohttp-3.12.14-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:938bd3ca6259e7e48b38d84f753d548bd863e0c222ed6ee6ace3fd6752768a84"}, + {file = "aiohttp-3.12.14-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8bc784302b6b9f163b54c4e93d7a6f09563bd01ff2b841b29ed3ac126e5040bf"}, + {file = "aiohttp-3.12.14-cp39-cp39-win32.whl", hash = "sha256:a3416f95961dd7d5393ecff99e3f41dc990fb72eda86c11f2a60308ac6dcd7a0"}, + {file = "aiohttp-3.12.14-cp39-cp39-win_amd64.whl", hash = "sha256:196858b8820d7f60578f8b47e5669b3195c21d8ab261e39b1d705346458f445f"}, + {file = "aiohttp-3.12.14.tar.gz", hash = "sha256:6e06e120e34d93100de448fd941522e11dafa78ef1a893c179901b7d66aa29f2"}, ] [package.dependencies] aiohappyeyeballs = ">=2.5.0" -aiosignal = ">=1.1.2" +aiosignal = ">=1.4.0" async-timeout = {version = ">=4.0,<6.0", markers = "python_version < \"3.11\""} attrs = ">=17.3.0" frozenlist = ">=1.1.1" @@ -123,18 +123,19 @@ speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (> [[package]] name = "aiosignal" -version = "1.3.2" +version = "1.4.0" description = "aiosignal: a list of registered asynchronous callbacks" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, - {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, + {file = "aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e"}, + {file = "aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7"}, ] [package.dependencies] frozenlist = ">=1.1.0" +typing-extensions = {version = ">=4.2", markers = "python_version < \"3.13\""} [[package]] name = "async-timeout" @@ -1633,11 +1634,11 @@ description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" groups = ["main", "dev"] -markers = "python_version < \"3.11\"" files = [ {file = "typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af"}, {file = "typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4"}, ] +markers = {main = "python_version < \"3.13\"", dev = "python_version < \"3.11\""} [[package]] name = "urllib3" From d463e7c745994e80036302202ee18e3b7ceaf6f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 2 Aug 2025 09:12:19 +0200 Subject: [PATCH 211/264] Bump aiohttp from 3.12.14 to 3.12.15 (#250) --- updated-dependencies: - dependency-name: aiohttp dependency-version: 3.12.15 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 174 ++++++++++++++++++++++++++-------------------------- 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/poetry.lock b/poetry.lock index 53e2b59..430599d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -14,98 +14,98 @@ files = [ [[package]] name = "aiohttp" -version = "3.12.14" +version = "3.12.15" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "aiohttp-3.12.14-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:906d5075b5ba0dd1c66fcaaf60eb09926a9fef3ca92d912d2a0bbdbecf8b1248"}, - {file = "aiohttp-3.12.14-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c875bf6fc2fd1a572aba0e02ef4e7a63694778c5646cdbda346ee24e630d30fb"}, - {file = "aiohttp-3.12.14-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fbb284d15c6a45fab030740049d03c0ecd60edad9cd23b211d7e11d3be8d56fd"}, - {file = "aiohttp-3.12.14-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38e360381e02e1a05d36b223ecab7bc4a6e7b5ab15760022dc92589ee1d4238c"}, - {file = "aiohttp-3.12.14-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:aaf90137b5e5d84a53632ad95ebee5c9e3e7468f0aab92ba3f608adcb914fa95"}, - {file = "aiohttp-3.12.14-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e532a25e4a0a2685fa295a31acf65e027fbe2bea7a4b02cdfbbba8a064577663"}, - {file = "aiohttp-3.12.14-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eab9762c4d1b08ae04a6c77474e6136da722e34fdc0e6d6eab5ee93ac29f35d1"}, - {file = "aiohttp-3.12.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abe53c3812b2899889a7fca763cdfaeee725f5be68ea89905e4275476ffd7e61"}, - {file = "aiohttp-3.12.14-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5760909b7080aa2ec1d320baee90d03b21745573780a072b66ce633eb77a8656"}, - {file = "aiohttp-3.12.14-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:02fcd3f69051467bbaa7f84d7ec3267478c7df18d68b2e28279116e29d18d4f3"}, - {file = "aiohttp-3.12.14-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4dcd1172cd6794884c33e504d3da3c35648b8be9bfa946942d353b939d5f1288"}, - {file = "aiohttp-3.12.14-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:224d0da41355b942b43ad08101b1b41ce633a654128ee07e36d75133443adcda"}, - {file = "aiohttp-3.12.14-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e387668724f4d734e865c1776d841ed75b300ee61059aca0b05bce67061dcacc"}, - {file = "aiohttp-3.12.14-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:dec9cde5b5a24171e0b0a4ca064b1414950904053fb77c707efd876a2da525d8"}, - {file = "aiohttp-3.12.14-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bbad68a2af4877cc103cd94af9160e45676fc6f0c14abb88e6e092b945c2c8e3"}, - {file = "aiohttp-3.12.14-cp310-cp310-win32.whl", hash = "sha256:ee580cb7c00bd857b3039ebca03c4448e84700dc1322f860cf7a500a6f62630c"}, - {file = "aiohttp-3.12.14-cp310-cp310-win_amd64.whl", hash = "sha256:cf4f05b8cea571e2ccc3ca744e35ead24992d90a72ca2cf7ab7a2efbac6716db"}, - {file = "aiohttp-3.12.14-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f4552ff7b18bcec18b60a90c6982049cdb9dac1dba48cf00b97934a06ce2e597"}, - {file = "aiohttp-3.12.14-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8283f42181ff6ccbcf25acaae4e8ab2ff7e92b3ca4a4ced73b2c12d8cd971393"}, - {file = "aiohttp-3.12.14-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:040afa180ea514495aaff7ad34ec3d27826eaa5d19812730fe9e529b04bb2179"}, - {file = "aiohttp-3.12.14-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b413c12f14c1149f0ffd890f4141a7471ba4b41234fe4fd4a0ff82b1dc299dbb"}, - {file = "aiohttp-3.12.14-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1d6f607ce2e1a93315414e3d448b831238f1874b9968e1195b06efaa5c87e245"}, - {file = "aiohttp-3.12.14-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:565e70d03e924333004ed101599902bba09ebb14843c8ea39d657f037115201b"}, - {file = "aiohttp-3.12.14-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4699979560728b168d5ab63c668a093c9570af2c7a78ea24ca5212c6cdc2b641"}, - {file = "aiohttp-3.12.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad5fdf6af93ec6c99bf800eba3af9a43d8bfd66dce920ac905c817ef4a712afe"}, - {file = "aiohttp-3.12.14-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ac76627c0b7ee0e80e871bde0d376a057916cb008a8f3ffc889570a838f5cc7"}, - {file = "aiohttp-3.12.14-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:798204af1180885651b77bf03adc903743a86a39c7392c472891649610844635"}, - {file = "aiohttp-3.12.14-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:4f1205f97de92c37dd71cf2d5bcfb65fdaed3c255d246172cce729a8d849b4da"}, - {file = "aiohttp-3.12.14-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:76ae6f1dd041f85065d9df77c6bc9c9703da9b5c018479d20262acc3df97d419"}, - {file = "aiohttp-3.12.14-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a194ace7bc43ce765338ca2dfb5661489317db216ea7ea700b0332878b392cab"}, - {file = "aiohttp-3.12.14-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:16260e8e03744a6fe3fcb05259eeab8e08342c4c33decf96a9dad9f1187275d0"}, - {file = "aiohttp-3.12.14-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8c779e5ebbf0e2e15334ea404fcce54009dc069210164a244d2eac8352a44b28"}, - {file = "aiohttp-3.12.14-cp311-cp311-win32.whl", hash = "sha256:a289f50bf1bd5be227376c067927f78079a7bdeccf8daa6a9e65c38bae14324b"}, - {file = "aiohttp-3.12.14-cp311-cp311-win_amd64.whl", hash = "sha256:0b8a69acaf06b17e9c54151a6c956339cf46db4ff72b3ac28516d0f7068f4ced"}, - {file = "aiohttp-3.12.14-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a0ecbb32fc3e69bc25efcda7d28d38e987d007096cbbeed04f14a6662d0eee22"}, - {file = "aiohttp-3.12.14-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0400f0ca9bb3e0b02f6466421f253797f6384e9845820c8b05e976398ac1d81a"}, - {file = "aiohttp-3.12.14-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a56809fed4c8a830b5cae18454b7464e1529dbf66f71c4772e3cfa9cbec0a1ff"}, - {file = "aiohttp-3.12.14-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27f2e373276e4755691a963e5d11756d093e346119f0627c2d6518208483fb6d"}, - {file = "aiohttp-3.12.14-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ca39e433630e9a16281125ef57ece6817afd1d54c9f1bf32e901f38f16035869"}, - {file = "aiohttp-3.12.14-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c748b3f8b14c77720132b2510a7d9907a03c20ba80f469e58d5dfd90c079a1c"}, - {file = "aiohttp-3.12.14-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0a568abe1b15ce69d4cc37e23020720423f0728e3cb1f9bcd3f53420ec3bfe7"}, - {file = "aiohttp-3.12.14-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9888e60c2c54eaf56704b17feb558c7ed6b7439bca1e07d4818ab878f2083660"}, - {file = "aiohttp-3.12.14-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3006a1dc579b9156de01e7916d38c63dc1ea0679b14627a37edf6151bc530088"}, - {file = "aiohttp-3.12.14-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aa8ec5c15ab80e5501a26719eb48a55f3c567da45c6ea5bb78c52c036b2655c7"}, - {file = "aiohttp-3.12.14-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:39b94e50959aa07844c7fe2206b9f75d63cc3ad1c648aaa755aa257f6f2498a9"}, - {file = "aiohttp-3.12.14-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:04c11907492f416dad9885d503fbfc5dcb6768d90cad8639a771922d584609d3"}, - {file = "aiohttp-3.12.14-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:88167bd9ab69bb46cee91bd9761db6dfd45b6e76a0438c7e884c3f8160ff21eb"}, - {file = "aiohttp-3.12.14-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:791504763f25e8f9f251e4688195e8b455f8820274320204f7eafc467e609425"}, - {file = "aiohttp-3.12.14-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2785b112346e435dd3a1a67f67713a3fe692d288542f1347ad255683f066d8e0"}, - {file = "aiohttp-3.12.14-cp312-cp312-win32.whl", hash = "sha256:15f5f4792c9c999a31d8decf444e79fcfd98497bf98e94284bf390a7bb8c1729"}, - {file = "aiohttp-3.12.14-cp312-cp312-win_amd64.whl", hash = "sha256:3b66e1a182879f579b105a80d5c4bd448b91a57e8933564bf41665064796a338"}, - {file = "aiohttp-3.12.14-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3143a7893d94dc82bc409f7308bc10d60285a3cd831a68faf1aa0836c5c3c767"}, - {file = "aiohttp-3.12.14-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3d62ac3d506cef54b355bd34c2a7c230eb693880001dfcda0bf88b38f5d7af7e"}, - {file = "aiohttp-3.12.14-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:48e43e075c6a438937c4de48ec30fa8ad8e6dfef122a038847456bfe7b947b63"}, - {file = "aiohttp-3.12.14-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:077b4488411a9724cecc436cbc8c133e0d61e694995b8de51aaf351c7578949d"}, - {file = "aiohttp-3.12.14-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d8c35632575653f297dcbc9546305b2c1133391089ab925a6a3706dfa775ccab"}, - {file = "aiohttp-3.12.14-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b8ce87963f0035c6834b28f061df90cf525ff7c9b6283a8ac23acee6502afd4"}, - {file = "aiohttp-3.12.14-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0a2cf66e32a2563bb0766eb24eae7e9a269ac0dc48db0aae90b575dc9583026"}, - {file = "aiohttp-3.12.14-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdea089caf6d5cde975084a884c72d901e36ef9c2fd972c9f51efbbc64e96fbd"}, - {file = "aiohttp-3.12.14-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a7865f27db67d49e81d463da64a59365ebd6b826e0e4847aa111056dcb9dc88"}, - {file = "aiohttp-3.12.14-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0ab5b38a6a39781d77713ad930cb5e7feea6f253de656a5f9f281a8f5931b086"}, - {file = "aiohttp-3.12.14-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:9b3b15acee5c17e8848d90a4ebc27853f37077ba6aec4d8cb4dbbea56d156933"}, - {file = "aiohttp-3.12.14-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e4c972b0bdaac167c1e53e16a16101b17c6d0ed7eac178e653a07b9f7fad7151"}, - {file = "aiohttp-3.12.14-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7442488b0039257a3bdbc55f7209587911f143fca11df9869578db6c26feeeb8"}, - {file = "aiohttp-3.12.14-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f68d3067eecb64c5e9bab4a26aa11bd676f4c70eea9ef6536b0a4e490639add3"}, - {file = "aiohttp-3.12.14-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f88d3704c8b3d598a08ad17d06006cb1ca52a1182291f04979e305c8be6c9758"}, - {file = "aiohttp-3.12.14-cp313-cp313-win32.whl", hash = "sha256:a3c99ab19c7bf375c4ae3debd91ca5d394b98b6089a03231d4c580ef3c2ae4c5"}, - {file = "aiohttp-3.12.14-cp313-cp313-win_amd64.whl", hash = "sha256:3f8aad695e12edc9d571f878c62bedc91adf30c760c8632f09663e5f564f4baa"}, - {file = "aiohttp-3.12.14-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b8cc6b05e94d837bcd71c6531e2344e1ff0fb87abe4ad78a9261d67ef5d83eae"}, - {file = "aiohttp-3.12.14-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1dcb015ac6a3b8facd3677597edd5ff39d11d937456702f0bb2b762e390a21b"}, - {file = "aiohttp-3.12.14-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3779ed96105cd70ee5e85ca4f457adbce3d9ff33ec3d0ebcdf6c5727f26b21b3"}, - {file = "aiohttp-3.12.14-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:717a0680729b4ebd7569c1dcd718c46b09b360745fd8eb12317abc74b14d14d0"}, - {file = "aiohttp-3.12.14-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b5dd3a2ef7c7e968dbbac8f5574ebeac4d2b813b247e8cec28174a2ba3627170"}, - {file = "aiohttp-3.12.14-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4710f77598c0092239bc12c1fcc278a444e16c7032d91babf5abbf7166463f7b"}, - {file = "aiohttp-3.12.14-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f3e9f75ae842a6c22a195d4a127263dbf87cbab729829e0bd7857fb1672400b2"}, - {file = "aiohttp-3.12.14-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f9c8d55d6802086edd188e3a7d85a77787e50d56ce3eb4757a3205fa4657922"}, - {file = "aiohttp-3.12.14-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:79b29053ff3ad307880d94562cca80693c62062a098a5776ea8ef5ef4b28d140"}, - {file = "aiohttp-3.12.14-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:23e1332fff36bebd3183db0c7a547a1da9d3b4091509f6d818e098855f2f27d3"}, - {file = "aiohttp-3.12.14-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:a564188ce831fd110ea76bcc97085dd6c625b427db3f1dbb14ca4baa1447dcbc"}, - {file = "aiohttp-3.12.14-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:a7a1b4302f70bb3ec40ca86de82def532c97a80db49cac6a6700af0de41af5ee"}, - {file = "aiohttp-3.12.14-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:1b07ccef62950a2519f9bfc1e5b294de5dd84329f444ca0b329605ea787a3de5"}, - {file = "aiohttp-3.12.14-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:938bd3ca6259e7e48b38d84f753d548bd863e0c222ed6ee6ace3fd6752768a84"}, - {file = "aiohttp-3.12.14-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8bc784302b6b9f163b54c4e93d7a6f09563bd01ff2b841b29ed3ac126e5040bf"}, - {file = "aiohttp-3.12.14-cp39-cp39-win32.whl", hash = "sha256:a3416f95961dd7d5393ecff99e3f41dc990fb72eda86c11f2a60308ac6dcd7a0"}, - {file = "aiohttp-3.12.14-cp39-cp39-win_amd64.whl", hash = "sha256:196858b8820d7f60578f8b47e5669b3195c21d8ab261e39b1d705346458f445f"}, - {file = "aiohttp-3.12.14.tar.gz", hash = "sha256:6e06e120e34d93100de448fd941522e11dafa78ef1a893c179901b7d66aa29f2"}, + {file = "aiohttp-3.12.15-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b6fc902bff74d9b1879ad55f5404153e2b33a82e72a95c89cec5eb6cc9e92fbc"}, + {file = "aiohttp-3.12.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:098e92835b8119b54c693f2f88a1dec690e20798ca5f5fe5f0520245253ee0af"}, + {file = "aiohttp-3.12.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:40b3fee496a47c3b4a39a731954c06f0bd9bd3e8258c059a4beb76ac23f8e421"}, + {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ce13fcfb0bb2f259fb42106cdc63fa5515fb85b7e87177267d89a771a660b79"}, + {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3beb14f053222b391bf9cf92ae82e0171067cc9c8f52453a0f1ec7c37df12a77"}, + {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c39e87afe48aa3e814cac5f535bc6199180a53e38d3f51c5e2530f5aa4ec58c"}, + {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5f1b4ce5bc528a6ee38dbf5f39bbf11dd127048726323b72b8e85769319ffc4"}, + {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1004e67962efabbaf3f03b11b4c43b834081c9e3f9b32b16a7d97d4708a9abe6"}, + {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8faa08fcc2e411f7ab91d1541d9d597d3a90e9004180edb2072238c085eac8c2"}, + {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fe086edf38b2222328cdf89af0dde2439ee173b8ad7cb659b4e4c6f385b2be3d"}, + {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:79b26fe467219add81d5e47b4a4ba0f2394e8b7c7c3198ed36609f9ba161aecb"}, + {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b761bac1192ef24e16706d761aefcb581438b34b13a2f069a6d343ec8fb693a5"}, + {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e153e8adacfe2af562861b72f8bc47f8a5c08e010ac94eebbe33dc21d677cd5b"}, + {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:fc49c4de44977aa8601a00edbf157e9a421f227aa7eb477d9e3df48343311065"}, + {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2776c7ec89c54a47029940177e75c8c07c29c66f73464784971d6a81904ce9d1"}, + {file = "aiohttp-3.12.15-cp310-cp310-win32.whl", hash = "sha256:2c7d81a277fa78b2203ab626ced1487420e8c11a8e373707ab72d189fcdad20a"}, + {file = "aiohttp-3.12.15-cp310-cp310-win_amd64.whl", hash = "sha256:83603f881e11f0f710f8e2327817c82e79431ec976448839f3cd05d7afe8f830"}, + {file = "aiohttp-3.12.15-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d3ce17ce0220383a0f9ea07175eeaa6aa13ae5a41f30bc61d84df17f0e9b1117"}, + {file = "aiohttp-3.12.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:010cc9bbd06db80fe234d9003f67e97a10fe003bfbedb40da7d71c1008eda0fe"}, + {file = "aiohttp-3.12.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3f9d7c55b41ed687b9d7165b17672340187f87a773c98236c987f08c858145a9"}, + {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc4fbc61bb3548d3b482f9ac7ddd0f18c67e4225aaa4e8552b9f1ac7e6bda9e5"}, + {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7fbc8a7c410bb3ad5d595bb7118147dfbb6449d862cc1125cf8867cb337e8728"}, + {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:74dad41b3458dbb0511e760fb355bb0b6689e0630de8a22b1b62a98777136e16"}, + {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b6f0af863cf17e6222b1735a756d664159e58855da99cfe965134a3ff63b0b0"}, + {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5b7fe4972d48a4da367043b8e023fb70a04d1490aa7d68800e465d1b97e493b"}, + {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6443cca89553b7a5485331bc9bedb2342b08d073fa10b8c7d1c60579c4a7b9bd"}, + {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c5f40ec615e5264f44b4282ee27628cea221fcad52f27405b80abb346d9f3f8"}, + {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:2abbb216a1d3a2fe86dbd2edce20cdc5e9ad0be6378455b05ec7f77361b3ab50"}, + {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:db71ce547012a5420a39c1b744d485cfb823564d01d5d20805977f5ea1345676"}, + {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ced339d7c9b5030abad5854aa5413a77565e5b6e6248ff927d3e174baf3badf7"}, + {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:7c7dd29c7b5bda137464dc9bfc738d7ceea46ff70309859ffde8c022e9b08ba7"}, + {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:421da6fd326460517873274875c6c5a18ff225b40da2616083c5a34a7570b685"}, + {file = "aiohttp-3.12.15-cp311-cp311-win32.whl", hash = "sha256:4420cf9d179ec8dfe4be10e7d0fe47d6d606485512ea2265b0d8c5113372771b"}, + {file = "aiohttp-3.12.15-cp311-cp311-win_amd64.whl", hash = "sha256:edd533a07da85baa4b423ee8839e3e91681c7bfa19b04260a469ee94b778bf6d"}, + {file = "aiohttp-3.12.15-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:802d3868f5776e28f7bf69d349c26fc0efadb81676d0afa88ed00d98a26340b7"}, + {file = "aiohttp-3.12.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2800614cd560287be05e33a679638e586a2d7401f4ddf99e304d98878c29444"}, + {file = "aiohttp-3.12.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8466151554b593909d30a0a125d638b4e5f3836e5aecde85b66b80ded1cb5b0d"}, + {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e5a495cb1be69dae4b08f35a6c4579c539e9b5706f606632102c0f855bcba7c"}, + {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6404dfc8cdde35c69aaa489bb3542fb86ef215fc70277c892be8af540e5e21c0"}, + {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ead1c00f8521a5c9070fcb88f02967b1d8a0544e6d85c253f6968b785e1a2ab"}, + {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6990ef617f14450bc6b34941dba4f12d5613cbf4e33805932f853fbd1cf18bfb"}, + {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd736ed420f4db2b8148b52b46b88ed038d0354255f9a73196b7bbce3ea97545"}, + {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c5092ce14361a73086b90c6efb3948ffa5be2f5b6fbcf52e8d8c8b8848bb97c"}, + {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aaa2234bb60c4dbf82893e934d8ee8dea30446f0647e024074237a56a08c01bd"}, + {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6d86a2fbdd14192e2f234a92d3b494dd4457e683ba07e5905a0b3ee25389ac9f"}, + {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a041e7e2612041a6ddf1c6a33b883be6a421247c7afd47e885969ee4cc58bd8d"}, + {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5015082477abeafad7203757ae44299a610e89ee82a1503e3d4184e6bafdd519"}, + {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:56822ff5ddfd1b745534e658faba944012346184fbfe732e0d6134b744516eea"}, + {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b2acbbfff69019d9014508c4ba0401822e8bae5a5fdc3b6814285b71231b60f3"}, + {file = "aiohttp-3.12.15-cp312-cp312-win32.whl", hash = "sha256:d849b0901b50f2185874b9a232f38e26b9b3d4810095a7572eacea939132d4e1"}, + {file = "aiohttp-3.12.15-cp312-cp312-win_amd64.whl", hash = "sha256:b390ef5f62bb508a9d67cb3bba9b8356e23b3996da7062f1a57ce1a79d2b3d34"}, + {file = "aiohttp-3.12.15-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9f922ffd05034d439dde1c77a20461cf4a1b0831e6caa26151fe7aa8aaebc315"}, + {file = "aiohttp-3.12.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2ee8a8ac39ce45f3e55663891d4b1d15598c157b4d494a4613e704c8b43112cd"}, + {file = "aiohttp-3.12.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3eae49032c29d356b94eee45a3f39fdf4b0814b397638c2f718e96cfadf4c4e4"}, + {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b97752ff12cc12f46a9b20327104448042fce5c33a624f88c18f66f9368091c7"}, + {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:894261472691d6fe76ebb7fcf2e5870a2ac284c7406ddc95823c8598a1390f0d"}, + {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5fa5d9eb82ce98959fc1031c28198b431b4d9396894f385cb63f1e2f3f20ca6b"}, + {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0fa751efb11a541f57db59c1dd821bec09031e01452b2b6217319b3a1f34f3d"}, + {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5346b93e62ab51ee2a9d68e8f73c7cf96ffb73568a23e683f931e52450e4148d"}, + {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:049ec0360f939cd164ecbfd2873eaa432613d5e77d6b04535e3d1fbae5a9e645"}, + {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b52dcf013b57464b6d1e51b627adfd69a8053e84b7103a7cd49c030f9ca44461"}, + {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:9b2af240143dd2765e0fb661fd0361a1b469cab235039ea57663cda087250ea9"}, + {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ac77f709a2cde2cc71257ab2d8c74dd157c67a0558a0d2799d5d571b4c63d44d"}, + {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:47f6b962246f0a774fbd3b6b7be25d59b06fdb2f164cf2513097998fc6a29693"}, + {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:760fb7db442f284996e39cf9915a94492e1896baac44f06ae551974907922b64"}, + {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad702e57dc385cae679c39d318def49aef754455f237499d5b99bea4ef582e51"}, + {file = "aiohttp-3.12.15-cp313-cp313-win32.whl", hash = "sha256:f813c3e9032331024de2eb2e32a88d86afb69291fbc37a3a3ae81cc9917fb3d0"}, + {file = "aiohttp-3.12.15-cp313-cp313-win_amd64.whl", hash = "sha256:1a649001580bdb37c6fdb1bebbd7e3bc688e8ec2b5c6f52edbb664662b17dc84"}, + {file = "aiohttp-3.12.15-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:691d203c2bdf4f4637792efbbcdcd157ae11e55eaeb5e9c360c1206fb03d4d98"}, + {file = "aiohttp-3.12.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8e995e1abc4ed2a454c731385bf4082be06f875822adc4c6d9eaadf96e20d406"}, + {file = "aiohttp-3.12.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bd44d5936ab3193c617bfd6c9a7d8d1085a8dc8c3f44d5f1dcf554d17d04cf7d"}, + {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46749be6e89cd78d6068cdf7da51dbcfa4321147ab8e4116ee6678d9a056a0cf"}, + {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0c643f4d75adea39e92c0f01b3fb83d57abdec8c9279b3078b68a3a52b3933b6"}, + {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0a23918fedc05806966a2438489dcffccbdf83e921a1170773b6178d04ade142"}, + {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:74bdd8c864b36c3673741023343565d95bfbd778ffe1eb4d412c135a28a8dc89"}, + {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a146708808c9b7a988a4af3821379e379e0f0e5e466ca31a73dbdd0325b0263"}, + {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7011a70b56facde58d6d26da4fec3280cc8e2a78c714c96b7a01a87930a9530"}, + {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3bdd6e17e16e1dbd3db74d7f989e8af29c4d2e025f9828e6ef45fbdee158ec75"}, + {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:57d16590a351dfc914670bd72530fd78344b885a00b250e992faea565b7fdc05"}, + {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:bc9a0f6569ff990e0bbd75506c8d8fe7214c8f6579cca32f0546e54372a3bb54"}, + {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:536ad7234747a37e50e7b6794ea868833d5220b49c92806ae2d7e8a9d6b5de02"}, + {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f0adb4177fa748072546fb650d9bd7398caaf0e15b370ed3317280b13f4083b0"}, + {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:14954a2988feae3987f1eb49c706bff39947605f4b6fa4027c1d75743723eb09"}, + {file = "aiohttp-3.12.15-cp39-cp39-win32.whl", hash = "sha256:b784d6ed757f27574dca1c336f968f4e81130b27595e458e69457e6878251f5d"}, + {file = "aiohttp-3.12.15-cp39-cp39-win_amd64.whl", hash = "sha256:86ceded4e78a992f835209e236617bffae649371c4a50d5e5a3987f237db84b8"}, + {file = "aiohttp-3.12.15.tar.gz", hash = "sha256:4fc61385e9c98d72fcdf47e6dd81833f47b2f77c114c29cd64a361be57a763a2"}, ] [package.dependencies] From 686acc0b26ef848db726dd7dd3685b7ba95dd7c4 Mon Sep 17 00:00:00 2001 From: Krzysztof <38418755+krismarc@users.noreply.github.com> Date: Thu, 7 Aug 2025 20:31:54 +0200 Subject: [PATCH 212/264] Fields support (#248) * Update entities.py Fields support. https://v3-apidocs.cloudfoundry.org/version/3.197.0/index.html#fields As specified in the API description. Navigable object would contain only data from includes returned by fields query params. * tests + removed additional quotation in entities.py * readme update * poetry corrections --------- Co-authored-by: Krzysztof Marciniak Co-authored-by: Krzysztof Marciniak (external) --- README.rst | 10 ++ cloudfoundry_client/v3/entities.py | 3 + .../GET_response_fields_space_and_org.json | 139 ++++++++++++++++++ .../GET_{id}_response_fields_space.json | 71 +++++++++ tests/v3/test_service_instances.py | 51 +++++++ 5 files changed, 274 insertions(+) create mode 100644 tests/fixtures/v3/service_instances/GET_response_fields_space_and_org.json create mode 100644 tests/fixtures/v3/service_instances/GET_{id}_response_fields_space.json diff --git a/README.rst b/README.rst index 164cee2..eb52a8e 100644 --- a/README.rst +++ b/README.rst @@ -252,6 +252,16 @@ By changing the first line only, a single request fetches all the data. The navi app = client.v3.apps.get("app-guid", include="space.organization") +.. code-block:: python + + fields = { + "space": ["guid,name,relationships.organization"], + "space.organization": ["guid","name"] + } + services_instances = client.v3.service_instances.list(fields=fields) + +Relationship object generated by `fields` will contain only attributes returned by the API (eg. name, guid). Please note relationship needs to be explicitly requested, otherwise it will be ignored and child object not created. + Available managers on API V3 are: - ``apps`` diff --git a/cloudfoundry_client/v3/entities.py b/cloudfoundry_client/v3/entities.py index f793f84..7fa7d23 100644 --- a/cloudfoundry_client/v3/entities.py +++ b/cloudfoundry_client/v3/entities.py @@ -300,6 +300,9 @@ def _append_encoded_parameter(parameters: List[str], args: Tuple[str, Any]) -> L parameter_name, parameter_value = args[0], args[1] if isinstance(parameter_value, (list, tuple)): parameters.append("%s=%s" % (parameter_name, quote(",".join(parameter_value)))) + elif isinstance(parameter_value, (dict)) and parameter_name == "fields": + for resource, key in parameter_value.items(): + parameters.append("%s[%s]=%s" % (parameter_name, resource, ",".join(key))) else: parameters.append("%s=%s" % (parameter_name, quote(str(parameter_value)))) return parameters diff --git a/tests/fixtures/v3/service_instances/GET_response_fields_space_and_org.json b/tests/fixtures/v3/service_instances/GET_response_fields_space_and_org.json new file mode 100644 index 0000000..6f0830e --- /dev/null +++ b/tests/fixtures/v3/service_instances/GET_response_fields_space_and_org.json @@ -0,0 +1,139 @@ +{ + "pagination": { + "total_results": 2, + "total_pages": 1, + "first": { + "href": "https://somewhere.com/v3/service_instances?fields%5Bspace%5D=guid%2Cname%2Crelationships.organization&fields%5Bspace.organization%5D=guid%2Cname&page=1&per_page=50" + }, + "last": { + "href": "https://somewhere.com/v3/service_instances?fields%5Bspace%5D=guid%2Cname%2Crelationships.organization&fields%5Bspace.organization%5D=guid%2Cname&page=1&per_page=50" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "147119a3-53e7-41af-8d06-46806695ae1a", + "created_at": "2025-07-30T07:55:53Z", + "updated_at": "2025-07-30T07:55:53Z", + "name": "my-user-provided-service", + "tags": [], + "last_operation": { + "type": "create", + "state": "succeeded", + "description": "Operation succeeded", + "updated_at": "2025-07-30T07:55:53Z", + "created_at": "2025-07-30T07:55:53Z" + }, + "type": "user-provided", + "syslog_drain_url": null, + "route_service_url": null, + "relationships": { + "space": { + "data": { + "guid": "aa3c5cfd-3f75-43f3-aac8-216fec6b3be5" + } + } + }, + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://somewhere.com/v3/service_instances/147119a3-53e7-41af-8d06-46806695ae1a" + }, + "space": { + "href": "https://somewhere.com/v3/spaces/aa3c5cfd-3f75-43f3-aac8-216fec6b3be5" + }, + "service_credential_bindings": { + "href": "https://somewhere.com/v3/service_credential_bindings?service_instance_guids=147119a3-53e7-41af-8d06-46806695ae1a" + }, + "service_route_bindings": { + "href": "https://somewhere.com/v3/service_route_bindings?service_instance_guids=147119a3-53e7-41af-8d06-46806695ae1a" + }, + "credentials": { + "href": "https://somewhere.com/v3/service_instances/147119a3-53e7-41af-8d06-46806695ae1a/credentials" + } + } + }, + { + "guid": "858e2101-ebb3-4c62-af6d-06e26bae744c", + "created_at": "2025-07-30T07:57:04Z", + "updated_at": "2025-07-30T07:57:05Z", + "name": "my-managed-service", + "tags": [], + "last_operation": { + "type": "create", + "state": "succeeded", + "description": "", + "updated_at": "2025-07-30T07:57:05Z", + "created_at": "2025-07-30T07:57:05Z" + }, + "type": "managed", + "maintenance_info": {}, + "upgrade_available": false, + "dashboard_url": null, + "relationships": { + "space": { + "data": { + "guid": "aa3c5cfd-3f75-43f3-aac8-216fec6b3be5" + } + }, + "service_plan": { + "data": { + "guid": "a73e54c2-cc12-4fc5-8f8d-4eec3e6c383c" + } + } + }, + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://somewhere.com/v3/service_instances/858e2101-ebb3-4c62-af6d-06e26bae744c" + }, + "space": { + "href": "https://somewhere.com/v3/spaces/aa3c5cfd-3f75-43f3-aac8-216fec6b3be5" + }, + "service_credential_bindings": { + "href": "https://somewhere.com/v3/service_credential_bindings?service_instance_guids=858e2101-ebb3-4c62-af6d-06e26bae744c" + }, + "service_route_bindings": { + "href": "https://somewhere.com/v3/service_route_bindings?service_instance_guids=858e2101-ebb3-4c62-af6d-06e26bae744c" + }, + "service_plan": { + "href": "https://somewhere.com/v3/service_plans/a73e54c2-cc12-4fc5-8f8d-4eec3e6c383c" + }, + "parameters": { + "href": "https://somewhere.com/v3/service_instances/858e2101-ebb3-4c62-af6d-06e26bae744c/parameters" + }, + "shared_spaces": { + "href": "https://somewhere.com/v3/service_instances/858e2101-ebb3-4c62-af6d-06e26bae744c/relationships/shared_spaces" + } + } + } + ], + "included": { + "spaces": [ + { + "guid": "aa3c5cfd-3f75-43f3-aac8-216fec6b3be5", + "name": "my_space", + "relationships": { + "organization": { + "data": { + "guid": "24ae9e5a-3f0c-4347-8d82-610877534c74" + } + } + } + } + ], + "organizations": [ + { + "guid": "24ae9e5a-3f0c-4347-8d82-610877534c74", + "name": "my_organization" + } + ] + } +} \ No newline at end of file diff --git a/tests/fixtures/v3/service_instances/GET_{id}_response_fields_space.json b/tests/fixtures/v3/service_instances/GET_{id}_response_fields_space.json new file mode 100644 index 0000000..edc3211 --- /dev/null +++ b/tests/fixtures/v3/service_instances/GET_{id}_response_fields_space.json @@ -0,0 +1,71 @@ +{ + "guid": "858e2101-ebb3-4c62-af6d-06e26bae744c", + "created_at": "2025-07-30T07:57:04Z", + "updated_at": "2025-07-30T07:57:05Z", + "name": "my-managed-service", + "tags": [], + "last_operation": { + "type": "create", + "state": "succeeded", + "description": "", + "updated_at": "2025-07-30T07:57:05Z", + "created_at": "2025-07-30T07:57:05Z" + }, + "type": "managed", + "maintenance_info": {}, + "upgrade_available": false, + "dashboard_url": null, + "relationships": { + "space": { + "data": { + "guid": "aa3c5cfd-3f75-43f3-aac8-216fec6b3be5" + } + }, + "service_plan": { + "data": { + "guid": "a73e54c2-cc12-4fc5-8f8d-4eec3e6c383c" + } + } + }, + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://somewhere.com/v3/service_instances/858e2101-ebb3-4c62-af6d-06e26bae744c" + }, + "space": { + "href": "https://somewhere.com/v3/spaces/aa3c5cfd-3f75-43f3-aac8-216fec6b3be5" + }, + "service_credential_bindings": { + "href": "https://somewhere.com/v3/service_credential_bindings?service_instance_guids=858e2101-ebb3-4c62-af6d-06e26bae744c" + }, + "service_route_bindings": { + "href": "https://somewhere.com/v3/service_route_bindings?service_instance_guids=858e2101-ebb3-4c62-af6d-06e26bae744c" + }, + "service_plan": { + "href": "https://somewhere.com/v3/service_plans/a73e54c2-cc12-4fc5-8f8d-4eec3e6c383c" + }, + "parameters": { + "href": "https://somewhere.com/v3/service_instances/858e2101-ebb3-4c62-af6d-06e26bae744c/parameters" + }, + "shared_spaces": { + "href": "https://somewhere.com/v3/service_instances/858e2101-ebb3-4c62-af6d-06e26bae744c/relationships/shared_spaces" + } + }, + "included": { + "spaces": [ + { + "guid": "aa3c5cfd-3f75-43f3-aac8-216fec6b3be5", + "name": "my_space" + } + ], + "organizations": [ + { + "guid": "24ae9e5a-3f0c-4347-8d82-610877534c74", + "name": "my_organization" + } + ] + } +} \ No newline at end of file diff --git a/tests/v3/test_service_instances.py b/tests/v3/test_service_instances.py index 4a08a0c..f461e5b 100644 --- a/tests/v3/test_service_instances.py +++ b/tests/v3/test_service_instances.py @@ -99,6 +99,57 @@ def test_get(self): self.assertEqual("service_instance_id", service_instance["guid"]) self.assertIsInstance(service_instance, Entity) + def test_get_fields_space(self): + self.client.get.return_value = self.mock_response( + "/v3/service_instances/service_instance_id" + "?fields[space]=guid,name,relationships.organization" + "&fields[space.organization]=guid,name", + HTTPStatus.OK, + None, + "v3", + "service_instances", + "GET_{id}_response_fields_space.json" + ) + fields = { + "space": ["guid,name,relationships.organization"], + "space.organization": ["guid", "name"], + } + space = self.client.v3.service_instances.get("service_instance_id", fields=fields).space() + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual("my_space", space["name"]) + self.assertIsInstance(space, Entity) + + def test_list_fields_space_and_org(self): + self.client.get.return_value = self.mock_response( + "/v3/service_instances" + "?fields[space]=guid,name,relationships.organization" + "&fields[space.organization]=guid,name", + HTTPStatus.OK, + None, + "v3", + "service_instances", + "GET_response_fields_space_and_org.json" + ) + fields = { + "space": ["guid,name,relationships.organization"], + "space.organization": ["guid", "name"] + } + all_spaces = [app.space() for app in self.client.v3.service_instances.list(fields=fields)] + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual(2, len(all_spaces)) + space1 = all_spaces[0] + self.assertEqual(space1["name"], "my_space") + space1_org = space1.organization() + self.assertEqual(space1_org["name"], "my_organization") + self.assertIsInstance(space1, Entity) + self.assertIsInstance(space1_org, Entity) + space2 = all_spaces[1] + self.assertEqual(space2["name"], "my_space") + space2_org = space2.organization() + self.assertEqual(space2_org["name"], "my_organization") + self.assertIsInstance(space2, Entity) + self.assertIsInstance(space2_org, Entity) + def test_get_then_credentials(self): get_service_instance = self.mock_response( "/v3/service_instances/service_instance_id", HTTPStatus.OK, None, "v3", "service_instances", "GET_{id}_response.json") From f866b1d25e277a96c2afdeced6026f5339274436 Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Tue, 19 Aug 2025 07:32:44 +0200 Subject: [PATCH 213/264] Prepare 1.39.0 version --- cloudfoundry_client/__init__.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudfoundry_client/__init__.py b/cloudfoundry_client/__init__.py index f468d98..41fbf2d 100644 --- a/cloudfoundry_client/__init__.py +++ b/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.38.6" +__version__ = "1.39.0" diff --git a/pyproject.toml b/pyproject.toml index c7e5400..dd2b994 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ exclude = ''' [tool.poetry] name = "cloudfoundry_client" -version = "1.38.6" +version = "1.39.0" description = "A client library for CloudFoundry" authors = ["Benjamin Einaudi "] readme = "README.rst" From e7c3bb8aff158bce446d43582cad3ae382be22c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 14 Sep 2025 08:47:26 +0200 Subject: [PATCH 214/264] Bump requests from 2.32.4 to 2.32.5 (#253) Bumps [requests](https://github.com/psf/requests) from 2.32.4 to 2.32.5. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.32.4...v2.32.5) --- updated-dependencies: - dependency-name: requests dependency-version: 2.32.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 430599d..5441afe 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1470,14 +1470,14 @@ md = ["cmarkgfm (>=0.8.0)"] [[package]] name = "requests" -version = "2.32.4" +version = "2.32.5" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main", "dev"] files = [ - {file = "requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c"}, - {file = "requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422"}, + {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"}, + {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"}, ] [package.dependencies] From 6b2f4d10a238cae7733605fd72cbfbdc368d3f2b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 14 Sep 2025 08:47:39 +0200 Subject: [PATCH 215/264] Bump protobuf from 6.31.1 to 6.32.0 (#252) Bumps [protobuf](https://github.com/protocolbuffers/protobuf) from 6.31.1 to 6.32.0. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Changelog](https://github.com/protocolbuffers/protobuf/blob/main/protobuf_release.bzl) - [Commits](https://github.com/protocolbuffers/protobuf/commits) --- updated-dependencies: - dependency-name: protobuf dependency-version: 6.32.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 22 +++++++++++----------- pyproject.toml | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/poetry.lock b/poetry.lock index 5441afe..168448d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1279,21 +1279,21 @@ files = [ [[package]] name = "protobuf" -version = "6.31.1" +version = "6.32.0" description = "" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "protobuf-6.31.1-cp310-abi3-win32.whl", hash = "sha256:7fa17d5a29c2e04b7d90e5e32388b8bfd0e7107cd8e616feef7ed3fa6bdab5c9"}, - {file = "protobuf-6.31.1-cp310-abi3-win_amd64.whl", hash = "sha256:426f59d2964864a1a366254fa703b8632dcec0790d8862d30034d8245e1cd447"}, - {file = "protobuf-6.31.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:6f1227473dc43d44ed644425268eb7c2e488ae245d51c6866d19fe158e207402"}, - {file = "protobuf-6.31.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:a40fc12b84c154884d7d4c4ebd675d5b3b5283e155f324049ae396b95ddebc39"}, - {file = "protobuf-6.31.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4ee898bf66f7a8b0bd21bce523814e6fbd8c6add948045ce958b73af7e8878c6"}, - {file = "protobuf-6.31.1-cp39-cp39-win32.whl", hash = "sha256:0414e3aa5a5f3ff423828e1e6a6e907d6c65c1d5b7e6e975793d5590bdeecc16"}, - {file = "protobuf-6.31.1-cp39-cp39-win_amd64.whl", hash = "sha256:8764cf4587791e7564051b35524b72844f845ad0bb011704c3736cce762d8fe9"}, - {file = "protobuf-6.31.1-py3-none-any.whl", hash = "sha256:720a6c7e6b77288b85063569baae8536671b39f15cc22037ec7045658d80489e"}, - {file = "protobuf-6.31.1.tar.gz", hash = "sha256:d8cac4c982f0b957a4dc73a80e2ea24fab08e679c0de9deb835f4a12d69aca9a"}, + {file = "protobuf-6.32.0-cp310-abi3-win32.whl", hash = "sha256:84f9e3c1ff6fb0308dbacb0950d8aa90694b0d0ee68e75719cb044b7078fe741"}, + {file = "protobuf-6.32.0-cp310-abi3-win_amd64.whl", hash = "sha256:a8bdbb2f009cfc22a36d031f22a625a38b615b5e19e558a7b756b3279723e68e"}, + {file = "protobuf-6.32.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d52691e5bee6c860fff9a1c86ad26a13afbeb4b168cd4445c922b7e2cf85aaf0"}, + {file = "protobuf-6.32.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:501fe6372fd1c8ea2a30b4d9be8f87955a64d6be9c88a973996cef5ef6f0abf1"}, + {file = "protobuf-6.32.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:75a2aab2bd1aeb1f5dc7c5f33bcb11d82ea8c055c9becbb41c26a8c43fd7092c"}, + {file = "protobuf-6.32.0-cp39-cp39-win32.whl", hash = "sha256:7db8ed09024f115ac877a1427557b838705359f047b2ff2f2b2364892d19dacb"}, + {file = "protobuf-6.32.0-cp39-cp39-win_amd64.whl", hash = "sha256:15eba1b86f193a407607112ceb9ea0ba9569aed24f93333fe9a497cf2fda37d3"}, + {file = "protobuf-6.32.0-py3-none-any.whl", hash = "sha256:ba377e5b67b908c8f3072a57b63e2c6a4cbd18aea4ed98d2584350dbf46f2783"}, + {file = "protobuf-6.32.0.tar.gz", hash = "sha256:a81439049127067fc49ec1d36e25c6ee1d1a2b7be930675f919258d03c04e7d2"}, ] [[package]] @@ -1818,4 +1818,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9" -content-hash = "db664a7a897340112cff748a823bc9b234ea51369783a5be2597d1216f0b0b4b" +content-hash = "9e8eb13ebf92fbfaa4671d289d9624904c65e3477a2f9e0d522d1f11466193ee" diff --git a/pyproject.toml b/pyproject.toml index dd2b994..b2cc40a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ keywords = ["cloudfoundry", "cf"] [tool.poetry.dependencies] python = ">=3.9" aiohttp = ">=3.8.0" -protobuf = "6.31.1" +protobuf = "6.32.0" oauth2-client= "1.4.2" websocket-client= "~1.8.0" PyYAML = ">=6.0" From 54274723906dd92500d7f18b4b22689ad7cdd660 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 14 Sep 2025 08:47:51 +0200 Subject: [PATCH 216/264] Bump actions/checkout from 4 to 5 (#251) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pythonpackage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 4be11e9..2a0241b 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -16,7 +16,7 @@ jobs: python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 From 62b39ce562143f4ab54ec3aaf1e88275ec7520cd Mon Sep 17 00:00:00 2001 From: Pascal Zimmermann Date: Fri, 5 Dec 2025 17:51:14 +0100 Subject: [PATCH 217/264] feat: Update the client and protobuf version (#263) * feat: Update the client and protobuf version * feat: Update the websocket-client version --- cloudfoundry_client/__init__.py | 2 +- poetry.lock | 41 +++++++++++++++++---------------- pyproject.toml | 6 ++--- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/cloudfoundry_client/__init__.py b/cloudfoundry_client/__init__.py index 41fbf2d..910720d 100644 --- a/cloudfoundry_client/__init__.py +++ b/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.39.0" +__version__ = "1.39.1" diff --git a/poetry.lock b/poetry.lock index 168448d..08658f1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -712,7 +712,7 @@ description = "Read metadata from Python packages" optional = false python-versions = ">=3.9" groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version < \"3.10\"" +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version == \"3.9\"" files = [ {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"}, {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"}, @@ -1279,21 +1279,22 @@ files = [ [[package]] name = "protobuf" -version = "6.32.0" +version = "6.33.0" description = "" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "protobuf-6.32.0-cp310-abi3-win32.whl", hash = "sha256:84f9e3c1ff6fb0308dbacb0950d8aa90694b0d0ee68e75719cb044b7078fe741"}, - {file = "protobuf-6.32.0-cp310-abi3-win_amd64.whl", hash = "sha256:a8bdbb2f009cfc22a36d031f22a625a38b615b5e19e558a7b756b3279723e68e"}, - {file = "protobuf-6.32.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d52691e5bee6c860fff9a1c86ad26a13afbeb4b168cd4445c922b7e2cf85aaf0"}, - {file = "protobuf-6.32.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:501fe6372fd1c8ea2a30b4d9be8f87955a64d6be9c88a973996cef5ef6f0abf1"}, - {file = "protobuf-6.32.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:75a2aab2bd1aeb1f5dc7c5f33bcb11d82ea8c055c9becbb41c26a8c43fd7092c"}, - {file = "protobuf-6.32.0-cp39-cp39-win32.whl", hash = "sha256:7db8ed09024f115ac877a1427557b838705359f047b2ff2f2b2364892d19dacb"}, - {file = "protobuf-6.32.0-cp39-cp39-win_amd64.whl", hash = "sha256:15eba1b86f193a407607112ceb9ea0ba9569aed24f93333fe9a497cf2fda37d3"}, - {file = "protobuf-6.32.0-py3-none-any.whl", hash = "sha256:ba377e5b67b908c8f3072a57b63e2c6a4cbd18aea4ed98d2584350dbf46f2783"}, - {file = "protobuf-6.32.0.tar.gz", hash = "sha256:a81439049127067fc49ec1d36e25c6ee1d1a2b7be930675f919258d03c04e7d2"}, + {file = "protobuf-6.33.0-cp310-abi3-win32.whl", hash = "sha256:d6101ded078042a8f17959eccd9236fb7a9ca20d3b0098bbcb91533a5680d035"}, + {file = "protobuf-6.33.0-cp310-abi3-win_amd64.whl", hash = "sha256:9a031d10f703f03768f2743a1c403af050b6ae1f3480e9c140f39c45f81b13ee"}, + {file = "protobuf-6.33.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:905b07a65f1a4b72412314082c7dbfae91a9e8b68a0cc1577515f8df58ecf455"}, + {file = "protobuf-6.33.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e0697ece353e6239b90ee43a9231318302ad8353c70e6e45499fa52396debf90"}, + {file = "protobuf-6.33.0-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:e0a1715e4f27355afd9570f3ea369735afc853a6c3951a6afe1f80d8569ad298"}, + {file = "protobuf-6.33.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:35be49fd3f4fefa4e6e2aacc35e8b837d6703c37a2168a55ac21e9b1bc7559ef"}, + {file = "protobuf-6.33.0-cp39-cp39-win32.whl", hash = "sha256:cd33a8e38ea3e39df66e1bbc462b076d6e5ba3a4ebbde58219d777223a7873d3"}, + {file = "protobuf-6.33.0-cp39-cp39-win_amd64.whl", hash = "sha256:c963e86c3655af3a917962c9619e1a6b9670540351d7af9439d06064e3317cc9"}, + {file = "protobuf-6.33.0-py3-none-any.whl", hash = "sha256:25c9e1963c6734448ea2d308cfa610e692b801304ba0908d7bfa564ac5132995"}, + {file = "protobuf-6.33.0.tar.gz", hash = "sha256:140303d5c8d2037730c548f8c7b93b20bb1dc301be280c378b82b8894589c954"}, ] [[package]] @@ -1660,20 +1661,20 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "websocket-client" -version = "1.8.0" +version = "1.9.0" description = "WebSocket client for Python with low level API options" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526"}, - {file = "websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da"}, + {file = "websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef"}, + {file = "websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98"}, ] [package.extras] -docs = ["Sphinx (>=6.0)", "myst-parser (>=2.0.0)", "sphinx-rtd-theme (>=1.1.0)"] +docs = ["Sphinx (>=6.0)", "myst-parser (>=2.0.0)", "sphinx_rtd_theme (>=1.1.0)"] optional = ["python-socks", "wsaccel"] -test = ["websockets"] +test = ["pytest", "websockets"] [[package]] name = "yarl" @@ -1801,7 +1802,7 @@ description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version < \"3.10\"" +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version == \"3.9\"" files = [ {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, @@ -1818,4 +1819,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9" -content-hash = "9e8eb13ebf92fbfaa4671d289d9624904c65e3477a2f9e0d522d1f11466193ee" +content-hash = "c47c40163d4f0711f33391d0d5c258ea67379c886298753c5170a82acb0eb2cb" diff --git a/pyproject.toml b/pyproject.toml index b2cc40a..b78f0b2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ exclude = ''' [tool.poetry] name = "cloudfoundry_client" -version = "1.39.0" +version = "1.39.1" description = "A client library for CloudFoundry" authors = ["Benjamin Einaudi "] readme = "README.rst" @@ -20,9 +20,9 @@ keywords = ["cloudfoundry", "cf"] [tool.poetry.dependencies] python = ">=3.9" aiohttp = ">=3.8.0" -protobuf = "6.32.0" +protobuf = "6.33.0" oauth2-client= "1.4.2" -websocket-client= "~1.8.0" +websocket-client= "~1.9.0" PyYAML = ">=6.0" requests = ">=2.5.0" polling2= "0.5.0" From 201bee9fe50b8bdda596ad2f8b4470fab62e5fbe Mon Sep 17 00:00:00 2001 From: David Duarte <102606398+daviddmd@users.noreply.github.com> Date: Fri, 5 Dec 2025 17:52:03 +0100 Subject: [PATCH 218/264] feat(apps): Add support for the apps revisions endpoints (#254) * fix(networking): Update entities _get_url_filtered function fix(networking): Update _append_encoded_parameter function to append the parameters with the %s=%s format, in order to be compliant with the Policy Server API interface * feat(apps): Add function for the /v3/apps/:guid/manifest endpoint * test: Add test for the v3 apps get_manifest function * test: Fix formatting of statements in test_get_manifest function * feat(apps): Update calls to _list * test(apps): Add unit tests for revisions and deployed revisions endpoints docs: Update README.rst * chore: Fix linting issues * fix: Rename from get to list revisions --- README.rst | 3 +- cloudfoundry_client/v3/apps.py | 12 +++- .../GET_{id}_deployed_revisions_response.json | 62 +++++++++++++++++++ .../v3/apps/GET_{id}_revisions_response.json | 62 +++++++++++++++++++ tests/v3/test_apps.py | 31 +++++++++- 5 files changed, 166 insertions(+), 4 deletions(-) create mode 100644 tests/fixtures/v3/apps/GET_{id}_deployed_revisions_response.json create mode 100644 tests/fixtures/v3/apps/GET_{id}_revisions_response.json diff --git a/README.rst b/README.rst index eb52a8e..e8466d6 100644 --- a/README.rst +++ b/README.rst @@ -397,4 +397,5 @@ You can run tests by doing so. In the project directory: $ export PYTHONPATH=main $ python -m unittest discover test # or even - $ python setup.py test + $ poetry install + $ poetry run pytest \ No newline at end of file diff --git a/cloudfoundry_client/v3/apps.py b/cloudfoundry_client/v3/apps.py index 4382ea2..5ec8486 100644 --- a/cloudfoundry_client/v3/apps.py +++ b/cloudfoundry_client/v3/apps.py @@ -1,7 +1,7 @@ from typing import TYPE_CHECKING, Optional -from cloudfoundry_client.common_objects import JsonObject -from cloudfoundry_client.v3.entities import EntityManager +from cloudfoundry_client.common_objects import JsonObject, Pagination +from cloudfoundry_client.v3.entities import EntityManager, Entity if TYPE_CHECKING: from cloudfoundry_client.client import CloudFoundryClient @@ -27,3 +27,11 @@ def get_routes(self, application_guid: str) -> JsonObject: def get_manifest(self, application_guid: str) -> str: return self.client.get(url="%s%s/%s/manifest" % (self.target_endpoint, self.entity_uri, application_guid)).text + + def list_revisions(self, application_guid: str, **kwargs) -> Pagination[Entity]: + uri: str = "%s/%s/revisions" % (self.entity_uri, application_guid) + return super(AppManager, self)._list(requested_path=uri, **kwargs) + + def list_deployed_revisions(self, application_guid: str, **kwargs) -> Pagination[Entity]: + uri: str = "%s/%s/revisions/deployed" % (self.entity_uri, application_guid) + return super(AppManager, self)._list(requested_path=uri, **kwargs) diff --git a/tests/fixtures/v3/apps/GET_{id}_deployed_revisions_response.json b/tests/fixtures/v3/apps/GET_{id}_deployed_revisions_response.json new file mode 100644 index 0000000..920f448 --- /dev/null +++ b/tests/fixtures/v3/apps/GET_{id}_deployed_revisions_response.json @@ -0,0 +1,62 @@ +{ + "pagination": { + "total_results": 1, + "total_pages": 1, + "first": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/revisions?page=1&per_page=50" + }, + "last": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/revisions?page=1&per_page=50" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "885735b5-aea4-4cf5-8e44-961af0e41920", + "version": 1, + "droplet": { + "guid": "585bc3c1-3743-497d-88b0-403ad6b56d16" + }, + "processes": { + "web": { + "command": "bundle exec rackup" + } + }, + "sidecars": [ + { + "name": "auth-sidecar", + "command": "bundle exec sidecar", + "process_types": ["web"], + "memory_in_mb": 300 + } + ], + "description": "Initial revision.", + "deployable": true, + "relationships": { + "app": { + "data": { + "guid": "1cb006ee-fb05-47e1-b541-c34179ddc446" + } + } + }, + "created_at": "2017-02-01T01:33:58Z", + "updated_at": "2017-02-01T01:33:58Z", + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/revisions/885735b5-aea4-4cf5-8e44-961af0e41920" + }, + "app": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446" + }, + "environment_variables": { + "href": "https://api.example.org/v3/revisions/885735b5-aea4-4cf5-8e44-961af0e41920/environment_variables" + } + } + } + ] +} \ No newline at end of file diff --git a/tests/fixtures/v3/apps/GET_{id}_revisions_response.json b/tests/fixtures/v3/apps/GET_{id}_revisions_response.json new file mode 100644 index 0000000..920f448 --- /dev/null +++ b/tests/fixtures/v3/apps/GET_{id}_revisions_response.json @@ -0,0 +1,62 @@ +{ + "pagination": { + "total_results": 1, + "total_pages": 1, + "first": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/revisions?page=1&per_page=50" + }, + "last": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/revisions?page=1&per_page=50" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "885735b5-aea4-4cf5-8e44-961af0e41920", + "version": 1, + "droplet": { + "guid": "585bc3c1-3743-497d-88b0-403ad6b56d16" + }, + "processes": { + "web": { + "command": "bundle exec rackup" + } + }, + "sidecars": [ + { + "name": "auth-sidecar", + "command": "bundle exec sidecar", + "process_types": ["web"], + "memory_in_mb": 300 + } + ], + "description": "Initial revision.", + "deployable": true, + "relationships": { + "app": { + "data": { + "guid": "1cb006ee-fb05-47e1-b541-c34179ddc446" + } + } + }, + "created_at": "2017-02-01T01:33:58Z", + "updated_at": "2017-02-01T01:33:58Z", + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/revisions/885735b5-aea4-4cf5-8e44-961af0e41920" + }, + "app": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446" + }, + "environment_variables": { + "href": "https://api.example.org/v3/revisions/885735b5-aea4-4cf5-8e44-961af0e41920/environment_variables" + } + } + } + ] +} \ No newline at end of file diff --git a/tests/v3/test_apps.py b/tests/v3/test_apps.py index 8aeb0cf..5c9ed9d 100644 --- a/tests/v3/test_apps.py +++ b/tests/v3/test_apps.py @@ -4,7 +4,7 @@ from typing import Optional, List, Union from abstract_test_case import AbstractTestCase -from cloudfoundry_client.common_objects import JsonObject +from cloudfoundry_client.common_objects import JsonObject, Pagination from cloudfoundry_client.v3.entities import Entity @@ -160,3 +160,32 @@ def test_get_manifest(self): self.assertIsInstance(application_route, dict) self.assertEqual(application_route.get("route"), "my-app.example.com") self.assertEqual(application_route.get("protocol"), "http1") + + def test_list_revisions(self): + self.client.get.return_value = self.mock_response( + "/v3/apps/app_guid/revisions", HTTPStatus.OK, {"Content-Type": "application/json"}, "v3", "apps", + "GET_{id}_revisions_response.json" + ) + revisions_response: Pagination[Entity] = self.client.v3.apps.list_revisions("app_guid") + revisions: list[dict] = [revision for revision in revisions_response] + self.assertIsInstance(revisions, list) + self.assertEqual(len(revisions), 1) + revision: dict = revisions[0] + self.assertIsInstance(revision, dict) + self.assertEqual(revision.get("guid"), "885735b5-aea4-4cf5-8e44-961af0e41920") + self.assertEqual(revision.get("description"), "Initial revision.") + self.assertEqual(revision.get("deployable"), True) + + def test_list_deployed_revisions(self): + self.client.get.return_value = self.mock_response( + "/v3/apps/app_guid/revisions/deployed", HTTPStatus.OK, {"Content-Type": "application/json"}, "v3", "apps", + "GET_{id}_deployed_revisions_response.json" + ) + revisions_response: Pagination[Entity] = self.client.v3.apps.list_deployed_revisions("app_guid") + revisions: list[dict] = [revision for revision in revisions_response] + self.assertIsInstance(revisions, list) + self.assertEqual(len(revisions), 1) + revision: dict = revisions[0] + self.assertIsInstance(revision, dict) + self.assertEqual(revision.get("created_at"), "2017-02-01T01:33:58Z") + self.assertEqual(revision.get("version"), 1) From 3c3535798deb98f60f8279aee01f04642ebc0e95 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Dec 2025 17:52:41 +0100 Subject: [PATCH 219/264] Bump actions/checkout from 5 to 6 (#262) Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pythonpackage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 2a0241b..ecc25a0 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -16,7 +16,7 @@ jobs: python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 From 13c2291fb1cb3d74f76011eb7bb42948f2861afb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Dec 2025 17:53:09 +0100 Subject: [PATCH 220/264] Bump actions/setup-python from 5 to 6 (#255) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pythonpackage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index ecc25a0..f8da6ee 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} From 117817c132c75a11db9187e64ed5a0371da6b2fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 6 Dec 2025 10:21:23 +0100 Subject: [PATCH 221/264] Bump urllib3 from 2.5.0 to 2.6.0 (#264) Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.5.0 to 2.6.0. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/2.5.0...2.6.0) --- updated-dependencies: - dependency-name: urllib3 dependency-version: 2.6.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index 08658f1..87dff80 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1643,21 +1643,21 @@ markers = {main = "python_version < \"3.13\"", dev = "python_version < \"3.11\"" [[package]] name = "urllib3" -version = "2.5.0" +version = "2.6.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" groups = ["main", "dev"] files = [ - {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, - {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, + {file = "urllib3-2.6.0-py3-none-any.whl", hash = "sha256:c90f7a39f716c572c4e3e58509581ebd83f9b59cced005b7db7ad2d22b0db99f"}, + {file = "urllib3-2.6.0.tar.gz", hash = "sha256:cb9bcef5a4b345d5da5d145dc3e30834f58e8018828cbc724d30b4cb7d4d49f1"}, ] [package.extras] -brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] +brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] +zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] [[package]] name = "websocket-client" From 509a8b6d029509de8bc7f869b43481b3c19d247f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jan 2026 23:24:56 +0100 Subject: [PATCH 222/264] Bump protobuf from 6.33.0 to 6.33.2 (#265) Bumps [protobuf](https://github.com/protocolbuffers/protobuf) from 6.33.0 to 6.33.2. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Commits](https://github.com/protocolbuffers/protobuf/commits) --- updated-dependencies: - dependency-name: protobuf dependency-version: 6.33.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 24 ++++++++++++------------ pyproject.toml | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/poetry.lock b/poetry.lock index 87dff80..e4b331f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1279,22 +1279,22 @@ files = [ [[package]] name = "protobuf" -version = "6.33.0" +version = "6.33.2" description = "" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "protobuf-6.33.0-cp310-abi3-win32.whl", hash = "sha256:d6101ded078042a8f17959eccd9236fb7a9ca20d3b0098bbcb91533a5680d035"}, - {file = "protobuf-6.33.0-cp310-abi3-win_amd64.whl", hash = "sha256:9a031d10f703f03768f2743a1c403af050b6ae1f3480e9c140f39c45f81b13ee"}, - {file = "protobuf-6.33.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:905b07a65f1a4b72412314082c7dbfae91a9e8b68a0cc1577515f8df58ecf455"}, - {file = "protobuf-6.33.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e0697ece353e6239b90ee43a9231318302ad8353c70e6e45499fa52396debf90"}, - {file = "protobuf-6.33.0-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:e0a1715e4f27355afd9570f3ea369735afc853a6c3951a6afe1f80d8569ad298"}, - {file = "protobuf-6.33.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:35be49fd3f4fefa4e6e2aacc35e8b837d6703c37a2168a55ac21e9b1bc7559ef"}, - {file = "protobuf-6.33.0-cp39-cp39-win32.whl", hash = "sha256:cd33a8e38ea3e39df66e1bbc462b076d6e5ba3a4ebbde58219d777223a7873d3"}, - {file = "protobuf-6.33.0-cp39-cp39-win_amd64.whl", hash = "sha256:c963e86c3655af3a917962c9619e1a6b9670540351d7af9439d06064e3317cc9"}, - {file = "protobuf-6.33.0-py3-none-any.whl", hash = "sha256:25c9e1963c6734448ea2d308cfa610e692b801304ba0908d7bfa564ac5132995"}, - {file = "protobuf-6.33.0.tar.gz", hash = "sha256:140303d5c8d2037730c548f8c7b93b20bb1dc301be280c378b82b8894589c954"}, + {file = "protobuf-6.33.2-cp310-abi3-win32.whl", hash = "sha256:87eb388bd2d0f78febd8f4c8779c79247b26a5befad525008e49a6955787ff3d"}, + {file = "protobuf-6.33.2-cp310-abi3-win_amd64.whl", hash = "sha256:fc2a0e8b05b180e5fc0dd1559fe8ebdae21a27e81ac77728fb6c42b12c7419b4"}, + {file = "protobuf-6.33.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d9b19771ca75935b3a4422957bc518b0cecb978b31d1dd12037b088f6bcc0e43"}, + {file = "protobuf-6.33.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:b5d3b5625192214066d99b2b605f5783483575656784de223f00a8d00754fc0e"}, + {file = "protobuf-6.33.2-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8cd7640aee0b7828b6d03ae518b5b4806fdfc1afe8de82f79c3454f8aef29872"}, + {file = "protobuf-6.33.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:1f8017c48c07ec5859106533b682260ba3d7c5567b1ca1f24297ce03384d1b4f"}, + {file = "protobuf-6.33.2-cp39-cp39-win32.whl", hash = "sha256:7109dcc38a680d033ffb8bf896727423528db9163be1b6a02d6a49606dcadbfe"}, + {file = "protobuf-6.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:2981c58f582f44b6b13173e12bb8656711189c2a70250845f264b877f00b1913"}, + {file = "protobuf-6.33.2-py3-none-any.whl", hash = "sha256:7636aad9bb01768870266de5dc009de2d1b936771b38a793f73cbbf279c91c5c"}, + {file = "protobuf-6.33.2.tar.gz", hash = "sha256:56dc370c91fbb8ac85bc13582c9e373569668a290aa2e66a590c2a0d35ddb9e4"}, ] [[package]] @@ -1819,4 +1819,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9" -content-hash = "c47c40163d4f0711f33391d0d5c258ea67379c886298753c5170a82acb0eb2cb" +content-hash = "41785a2d2ed6e3a7804f16a647e253877e0a17f66d8496bda68ff1a89189d3b0" diff --git a/pyproject.toml b/pyproject.toml index b78f0b2..18014f0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ keywords = ["cloudfoundry", "cf"] [tool.poetry.dependencies] python = ">=3.9" aiohttp = ">=3.8.0" -protobuf = "6.33.0" +protobuf = "6.33.2" oauth2-client= "1.4.2" websocket-client= "~1.9.0" PyYAML = ">=6.0" From 19b70f164985014180f46a3ba82a2f809b8e1d23 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jan 2026 23:29:34 +0100 Subject: [PATCH 223/264] Bump black from 25.1.0 to 25.11.0 (#266) Bumps [black](https://github.com/psf/black) from 25.1.0 to 25.11.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/25.1.0...25.11.0) --- updated-dependencies: - dependency-name: black dependency-version: 25.11.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 68 ++++++++++++++++++++++++++++++++------------------ pyproject.toml | 2 +- 2 files changed, 45 insertions(+), 25 deletions(-) diff --git a/poetry.lock b/poetry.lock index e4b331f..179bf80 100644 --- a/poetry.lock +++ b/poetry.lock @@ -189,34 +189,38 @@ testing = ["jaraco.test", "pytest (!=8.0.*)", "pytest (>=6,!=8.1.*)", "pytest-ch [[package]] name = "black" -version = "25.1.0" +version = "25.11.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32"}, - {file = "black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da"}, - {file = "black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7"}, - {file = "black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9"}, - {file = "black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0"}, - {file = "black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299"}, - {file = "black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096"}, - {file = "black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2"}, - {file = "black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b"}, - {file = "black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc"}, - {file = "black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f"}, - {file = "black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba"}, - {file = "black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f"}, - {file = "black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3"}, - {file = "black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171"}, - {file = "black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18"}, - {file = "black-25.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1ee0a0c330f7b5130ce0caed9936a904793576ef4d2b98c40835d6a65afa6a0"}, - {file = "black-25.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3df5f1bf91d36002b0a75389ca8663510cf0531cca8aa5c1ef695b46d98655f"}, - {file = "black-25.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9e6827d563a2c820772b32ce8a42828dc6790f095f441beef18f96aa6f8294e"}, - {file = "black-25.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:bacabb307dca5ebaf9c118d2d2f6903da0d62c9faa82bd21a33eecc319559355"}, - {file = "black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717"}, - {file = "black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666"}, + {file = "black-25.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ec311e22458eec32a807f029b2646f661e6859c3f61bc6d9ffb67958779f392e"}, + {file = "black-25.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1032639c90208c15711334d681de2e24821af0575573db2810b0763bcd62e0f0"}, + {file = "black-25.11.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0c0f7c461df55cf32929b002335883946a4893d759f2df343389c4396f3b6b37"}, + {file = "black-25.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:f9786c24d8e9bd5f20dc7a7f0cdd742644656987f6ea6947629306f937726c03"}, + {file = "black-25.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:895571922a35434a9d8ca67ef926da6bc9ad464522a5fe0db99b394ef1c0675a"}, + {file = "black-25.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cb4f4b65d717062191bdec8e4a442539a8ea065e6af1c4f4d36f0cdb5f71e170"}, + {file = "black-25.11.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d81a44cbc7e4f73a9d6ae449ec2317ad81512d1e7dce7d57f6333fd6259737bc"}, + {file = "black-25.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:7eebd4744dfe92ef1ee349dc532defbf012a88b087bb7ddd688ff59a447b080e"}, + {file = "black-25.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:80e7486ad3535636657aa180ad32a7d67d7c273a80e12f1b4bfa0823d54e8fac"}, + {file = "black-25.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6cced12b747c4c76bc09b4db057c319d8545307266f41aaee665540bc0e04e96"}, + {file = "black-25.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cb2d54a39e0ef021d6c5eef442e10fd71fcb491be6413d083a320ee768329dd"}, + {file = "black-25.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae263af2f496940438e5be1a0c1020e13b09154f3af4df0835ea7f9fe7bfa409"}, + {file = "black-25.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0a1d40348b6621cc20d3d7530a5b8d67e9714906dfd7346338249ad9c6cedf2b"}, + {file = "black-25.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:51c65d7d60bb25429ea2bf0731c32b2a2442eb4bd3b2afcb47830f0b13e58bfd"}, + {file = "black-25.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:936c4dd07669269f40b497440159a221ee435e3fddcf668e0c05244a9be71993"}, + {file = "black-25.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:f42c0ea7f59994490f4dccd64e6b2dd49ac57c7c84f38b8faab50f8759db245c"}, + {file = "black-25.11.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:35690a383f22dd3e468c85dc4b915217f87667ad9cce781d7b42678ce63c4170"}, + {file = "black-25.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dae49ef7369c6caa1a1833fd5efb7c3024bb7e4499bf64833f65ad27791b1545"}, + {file = "black-25.11.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bd4a22a0b37401c8e492e994bce79e614f91b14d9ea911f44f36e262195fdda"}, + {file = "black-25.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:aa211411e94fdf86519996b7f5f05e71ba34835d8f0c0f03c00a26271da02664"}, + {file = "black-25.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a3bb5ce32daa9ff0605d73b6f19da0b0e6c1f8f2d75594db539fdfed722f2b06"}, + {file = "black-25.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9815ccee1e55717fe9a4b924cae1646ef7f54e0f990da39a34fc7b264fcf80a2"}, + {file = "black-25.11.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:92285c37b93a1698dcbc34581867b480f1ba3a7b92acf1fe0467b04d7a4da0dc"}, + {file = "black-25.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:43945853a31099c7c0ff8dface53b4de56c41294fa6783c0441a8b1d9bf668bc"}, + {file = "black-25.11.0-py3-none-any.whl", hash = "sha256:e3f562da087791e96cefcd9dda058380a442ab322a02e222add53736451f604b"}, + {file = "black-25.11.0.tar.gz", hash = "sha256:9a323ac32f5dc75ce7470501b887250be5005a01602e931a15e45593f70f6e08"}, ] [package.dependencies] @@ -225,6 +229,7 @@ mypy-extensions = ">=0.4.3" packaging = ">=22.0" pathspec = ">=0.9.0" platformdirs = ">=2" +pytokens = ">=0.3.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} @@ -1373,6 +1378,21 @@ tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] +[[package]] +name = "pytokens" +version = "0.3.0" +description = "A Fast, spec compliant Python 3.14+ tokenizer that runs on older Pythons." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pytokens-0.3.0-py3-none-any.whl", hash = "sha256:95b2b5eaf832e469d141a378872480ede3f251a5a5041b8ec6e581d3ac71bbf3"}, + {file = "pytokens-0.3.0.tar.gz", hash = "sha256:2f932b14ed08de5fcf0b391ace2642f858f1394c0857202959000b68ed7a458a"}, +] + +[package.extras] +dev = ["black", "build", "mypy", "pytest", "pytest-cov", "setuptools", "tox", "twine", "wheel"] + [[package]] name = "pywin32-ctypes" version = "0.2.3" @@ -1819,4 +1839,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9" -content-hash = "41785a2d2ed6e3a7804f16a647e253877e0a17f66d8496bda68ff1a89189d3b0" +content-hash = "daa72b1d19fdf9cc4af9342c3e985dec611b1bc55c63b95acb36497e59575f3a" diff --git a/pyproject.toml b/pyproject.toml index 18014f0..49d8b03 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ requests = ">=2.5.0" polling2= "0.5.0" [tool.poetry.group.dev.dependencies] -black= "25.1.0" +black= "25.11.0" flake8= "7.3.0" pytest = ">=8.2.2,<8.5.0" twine = ">=6.0,<6.2" From 714f514966262bf26113db3bbfc3acb739f2e70a Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Sun, 4 Jan 2026 12:16:13 +0100 Subject: [PATCH 224/264] Various updates (#267) aiohttp 3.13.3 PyYAML 6.0.3 pytest 8.4.2 twine 6.2.0 --- poetry.lock | 2126 ++++++++++++++++++++++++++++-------------------- pyproject.toml | 2 +- 2 files changed, 1255 insertions(+), 873 deletions(-) diff --git a/poetry.lock b/poetry.lock index 179bf80..ca813d8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -14,98 +14,132 @@ files = [ [[package]] name = "aiohttp" -version = "3.12.15" +version = "3.13.3" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "aiohttp-3.12.15-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b6fc902bff74d9b1879ad55f5404153e2b33a82e72a95c89cec5eb6cc9e92fbc"}, - {file = "aiohttp-3.12.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:098e92835b8119b54c693f2f88a1dec690e20798ca5f5fe5f0520245253ee0af"}, - {file = "aiohttp-3.12.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:40b3fee496a47c3b4a39a731954c06f0bd9bd3e8258c059a4beb76ac23f8e421"}, - {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ce13fcfb0bb2f259fb42106cdc63fa5515fb85b7e87177267d89a771a660b79"}, - {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3beb14f053222b391bf9cf92ae82e0171067cc9c8f52453a0f1ec7c37df12a77"}, - {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c39e87afe48aa3e814cac5f535bc6199180a53e38d3f51c5e2530f5aa4ec58c"}, - {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5f1b4ce5bc528a6ee38dbf5f39bbf11dd127048726323b72b8e85769319ffc4"}, - {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1004e67962efabbaf3f03b11b4c43b834081c9e3f9b32b16a7d97d4708a9abe6"}, - {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8faa08fcc2e411f7ab91d1541d9d597d3a90e9004180edb2072238c085eac8c2"}, - {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fe086edf38b2222328cdf89af0dde2439ee173b8ad7cb659b4e4c6f385b2be3d"}, - {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:79b26fe467219add81d5e47b4a4ba0f2394e8b7c7c3198ed36609f9ba161aecb"}, - {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b761bac1192ef24e16706d761aefcb581438b34b13a2f069a6d343ec8fb693a5"}, - {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e153e8adacfe2af562861b72f8bc47f8a5c08e010ac94eebbe33dc21d677cd5b"}, - {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:fc49c4de44977aa8601a00edbf157e9a421f227aa7eb477d9e3df48343311065"}, - {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2776c7ec89c54a47029940177e75c8c07c29c66f73464784971d6a81904ce9d1"}, - {file = "aiohttp-3.12.15-cp310-cp310-win32.whl", hash = "sha256:2c7d81a277fa78b2203ab626ced1487420e8c11a8e373707ab72d189fcdad20a"}, - {file = "aiohttp-3.12.15-cp310-cp310-win_amd64.whl", hash = "sha256:83603f881e11f0f710f8e2327817c82e79431ec976448839f3cd05d7afe8f830"}, - {file = "aiohttp-3.12.15-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d3ce17ce0220383a0f9ea07175eeaa6aa13ae5a41f30bc61d84df17f0e9b1117"}, - {file = "aiohttp-3.12.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:010cc9bbd06db80fe234d9003f67e97a10fe003bfbedb40da7d71c1008eda0fe"}, - {file = "aiohttp-3.12.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3f9d7c55b41ed687b9d7165b17672340187f87a773c98236c987f08c858145a9"}, - {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc4fbc61bb3548d3b482f9ac7ddd0f18c67e4225aaa4e8552b9f1ac7e6bda9e5"}, - {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7fbc8a7c410bb3ad5d595bb7118147dfbb6449d862cc1125cf8867cb337e8728"}, - {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:74dad41b3458dbb0511e760fb355bb0b6689e0630de8a22b1b62a98777136e16"}, - {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b6f0af863cf17e6222b1735a756d664159e58855da99cfe965134a3ff63b0b0"}, - {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5b7fe4972d48a4da367043b8e023fb70a04d1490aa7d68800e465d1b97e493b"}, - {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6443cca89553b7a5485331bc9bedb2342b08d073fa10b8c7d1c60579c4a7b9bd"}, - {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c5f40ec615e5264f44b4282ee27628cea221fcad52f27405b80abb346d9f3f8"}, - {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:2abbb216a1d3a2fe86dbd2edce20cdc5e9ad0be6378455b05ec7f77361b3ab50"}, - {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:db71ce547012a5420a39c1b744d485cfb823564d01d5d20805977f5ea1345676"}, - {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ced339d7c9b5030abad5854aa5413a77565e5b6e6248ff927d3e174baf3badf7"}, - {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:7c7dd29c7b5bda137464dc9bfc738d7ceea46ff70309859ffde8c022e9b08ba7"}, - {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:421da6fd326460517873274875c6c5a18ff225b40da2616083c5a34a7570b685"}, - {file = "aiohttp-3.12.15-cp311-cp311-win32.whl", hash = "sha256:4420cf9d179ec8dfe4be10e7d0fe47d6d606485512ea2265b0d8c5113372771b"}, - {file = "aiohttp-3.12.15-cp311-cp311-win_amd64.whl", hash = "sha256:edd533a07da85baa4b423ee8839e3e91681c7bfa19b04260a469ee94b778bf6d"}, - {file = "aiohttp-3.12.15-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:802d3868f5776e28f7bf69d349c26fc0efadb81676d0afa88ed00d98a26340b7"}, - {file = "aiohttp-3.12.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2800614cd560287be05e33a679638e586a2d7401f4ddf99e304d98878c29444"}, - {file = "aiohttp-3.12.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8466151554b593909d30a0a125d638b4e5f3836e5aecde85b66b80ded1cb5b0d"}, - {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e5a495cb1be69dae4b08f35a6c4579c539e9b5706f606632102c0f855bcba7c"}, - {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6404dfc8cdde35c69aaa489bb3542fb86ef215fc70277c892be8af540e5e21c0"}, - {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ead1c00f8521a5c9070fcb88f02967b1d8a0544e6d85c253f6968b785e1a2ab"}, - {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6990ef617f14450bc6b34941dba4f12d5613cbf4e33805932f853fbd1cf18bfb"}, - {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd736ed420f4db2b8148b52b46b88ed038d0354255f9a73196b7bbce3ea97545"}, - {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c5092ce14361a73086b90c6efb3948ffa5be2f5b6fbcf52e8d8c8b8848bb97c"}, - {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aaa2234bb60c4dbf82893e934d8ee8dea30446f0647e024074237a56a08c01bd"}, - {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6d86a2fbdd14192e2f234a92d3b494dd4457e683ba07e5905a0b3ee25389ac9f"}, - {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a041e7e2612041a6ddf1c6a33b883be6a421247c7afd47e885969ee4cc58bd8d"}, - {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5015082477abeafad7203757ae44299a610e89ee82a1503e3d4184e6bafdd519"}, - {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:56822ff5ddfd1b745534e658faba944012346184fbfe732e0d6134b744516eea"}, - {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b2acbbfff69019d9014508c4ba0401822e8bae5a5fdc3b6814285b71231b60f3"}, - {file = "aiohttp-3.12.15-cp312-cp312-win32.whl", hash = "sha256:d849b0901b50f2185874b9a232f38e26b9b3d4810095a7572eacea939132d4e1"}, - {file = "aiohttp-3.12.15-cp312-cp312-win_amd64.whl", hash = "sha256:b390ef5f62bb508a9d67cb3bba9b8356e23b3996da7062f1a57ce1a79d2b3d34"}, - {file = "aiohttp-3.12.15-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9f922ffd05034d439dde1c77a20461cf4a1b0831e6caa26151fe7aa8aaebc315"}, - {file = "aiohttp-3.12.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2ee8a8ac39ce45f3e55663891d4b1d15598c157b4d494a4613e704c8b43112cd"}, - {file = "aiohttp-3.12.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3eae49032c29d356b94eee45a3f39fdf4b0814b397638c2f718e96cfadf4c4e4"}, - {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b97752ff12cc12f46a9b20327104448042fce5c33a624f88c18f66f9368091c7"}, - {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:894261472691d6fe76ebb7fcf2e5870a2ac284c7406ddc95823c8598a1390f0d"}, - {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5fa5d9eb82ce98959fc1031c28198b431b4d9396894f385cb63f1e2f3f20ca6b"}, - {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0fa751efb11a541f57db59c1dd821bec09031e01452b2b6217319b3a1f34f3d"}, - {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5346b93e62ab51ee2a9d68e8f73c7cf96ffb73568a23e683f931e52450e4148d"}, - {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:049ec0360f939cd164ecbfd2873eaa432613d5e77d6b04535e3d1fbae5a9e645"}, - {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b52dcf013b57464b6d1e51b627adfd69a8053e84b7103a7cd49c030f9ca44461"}, - {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:9b2af240143dd2765e0fb661fd0361a1b469cab235039ea57663cda087250ea9"}, - {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ac77f709a2cde2cc71257ab2d8c74dd157c67a0558a0d2799d5d571b4c63d44d"}, - {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:47f6b962246f0a774fbd3b6b7be25d59b06fdb2f164cf2513097998fc6a29693"}, - {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:760fb7db442f284996e39cf9915a94492e1896baac44f06ae551974907922b64"}, - {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad702e57dc385cae679c39d318def49aef754455f237499d5b99bea4ef582e51"}, - {file = "aiohttp-3.12.15-cp313-cp313-win32.whl", hash = "sha256:f813c3e9032331024de2eb2e32a88d86afb69291fbc37a3a3ae81cc9917fb3d0"}, - {file = "aiohttp-3.12.15-cp313-cp313-win_amd64.whl", hash = "sha256:1a649001580bdb37c6fdb1bebbd7e3bc688e8ec2b5c6f52edbb664662b17dc84"}, - {file = "aiohttp-3.12.15-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:691d203c2bdf4f4637792efbbcdcd157ae11e55eaeb5e9c360c1206fb03d4d98"}, - {file = "aiohttp-3.12.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8e995e1abc4ed2a454c731385bf4082be06f875822adc4c6d9eaadf96e20d406"}, - {file = "aiohttp-3.12.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bd44d5936ab3193c617bfd6c9a7d8d1085a8dc8c3f44d5f1dcf554d17d04cf7d"}, - {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46749be6e89cd78d6068cdf7da51dbcfa4321147ab8e4116ee6678d9a056a0cf"}, - {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0c643f4d75adea39e92c0f01b3fb83d57abdec8c9279b3078b68a3a52b3933b6"}, - {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0a23918fedc05806966a2438489dcffccbdf83e921a1170773b6178d04ade142"}, - {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:74bdd8c864b36c3673741023343565d95bfbd778ffe1eb4d412c135a28a8dc89"}, - {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a146708808c9b7a988a4af3821379e379e0f0e5e466ca31a73dbdd0325b0263"}, - {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7011a70b56facde58d6d26da4fec3280cc8e2a78c714c96b7a01a87930a9530"}, - {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3bdd6e17e16e1dbd3db74d7f989e8af29c4d2e025f9828e6ef45fbdee158ec75"}, - {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:57d16590a351dfc914670bd72530fd78344b885a00b250e992faea565b7fdc05"}, - {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:bc9a0f6569ff990e0bbd75506c8d8fe7214c8f6579cca32f0546e54372a3bb54"}, - {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:536ad7234747a37e50e7b6794ea868833d5220b49c92806ae2d7e8a9d6b5de02"}, - {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f0adb4177fa748072546fb650d9bd7398caaf0e15b370ed3317280b13f4083b0"}, - {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:14954a2988feae3987f1eb49c706bff39947605f4b6fa4027c1d75743723eb09"}, - {file = "aiohttp-3.12.15-cp39-cp39-win32.whl", hash = "sha256:b784d6ed757f27574dca1c336f968f4e81130b27595e458e69457e6878251f5d"}, - {file = "aiohttp-3.12.15-cp39-cp39-win_amd64.whl", hash = "sha256:86ceded4e78a992f835209e236617bffae649371c4a50d5e5a3987f237db84b8"}, - {file = "aiohttp-3.12.15.tar.gz", hash = "sha256:4fc61385e9c98d72fcdf47e6dd81833f47b2f77c114c29cd64a361be57a763a2"}, + {file = "aiohttp-3.13.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a372fd5afd301b3a89582817fdcdb6c34124787c70dbcc616f259013e7eef7"}, + {file = "aiohttp-3.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:147e422fd1223005c22b4fe080f5d93ced44460f5f9c105406b753612b587821"}, + {file = "aiohttp-3.13.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:859bd3f2156e81dd01432f5849fc73e2243d4a487c4fd26609b1299534ee1845"}, + {file = "aiohttp-3.13.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dca68018bf48c251ba17c72ed479f4dafe9dbd5a73707ad8d28a38d11f3d42af"}, + {file = "aiohttp-3.13.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fee0c6bc7db1de362252affec009707a17478a00ec69f797d23ca256e36d5940"}, + {file = "aiohttp-3.13.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c048058117fd649334d81b4b526e94bde3ccaddb20463a815ced6ecbb7d11160"}, + {file = "aiohttp-3.13.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:215a685b6fbbfcf71dfe96e3eba7a6f58f10da1dfdf4889c7dd856abe430dca7"}, + {file = "aiohttp-3.13.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2c184bb1fe2cbd2cefba613e9db29a5ab559323f994b6737e370d3da0ac455"}, + {file = "aiohttp-3.13.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75ca857eba4e20ce9f546cd59c7007b33906a4cd48f2ff6ccf1ccfc3b646f279"}, + {file = "aiohttp-3.13.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81e97251d9298386c2b7dbeb490d3d1badbdc69107fb8c9299dd04eb39bddc0e"}, + {file = "aiohttp-3.13.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c0e2d366af265797506f0283487223146af57815b388623f0357ef7eac9b209d"}, + {file = "aiohttp-3.13.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4e239d501f73d6db1522599e14b9b321a7e3b1de66ce33d53a765d975e9f4808"}, + {file = "aiohttp-3.13.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0db318f7a6f065d84cb1e02662c526294450b314a02bd9e2a8e67f0d8564ce40"}, + {file = "aiohttp-3.13.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:bfc1cc2fe31a6026a8a88e4ecfb98d7f6b1fec150cfd708adbfd1d2f42257c29"}, + {file = "aiohttp-3.13.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af71fff7bac6bb7508956696dce8f6eec2bbb045eceb40343944b1ae62b5ef11"}, + {file = "aiohttp-3.13.3-cp310-cp310-win32.whl", hash = "sha256:37da61e244d1749798c151421602884db5270faf479cf0ef03af0ff68954c9dd"}, + {file = "aiohttp-3.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:7e63f210bc1b57ef699035f2b4b6d9ce096b5914414a49b0997c839b2bd2223c"}, + {file = "aiohttp-3.13.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b6073099fb654e0a068ae678b10feff95c5cae95bbfcbfa7af669d361a8aa6b"}, + {file = "aiohttp-3.13.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cb93e166e6c28716c8c6aeb5f99dfb6d5ccf482d29fe9bf9a794110e6d0ab64"}, + {file = "aiohttp-3.13.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:28e027cf2f6b641693a09f631759b4d9ce9165099d2b5d92af9bd4e197690eea"}, + {file = "aiohttp-3.13.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3b61b7169ababd7802f9568ed96142616a9118dd2be0d1866e920e77ec8fa92a"}, + {file = "aiohttp-3.13.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:80dd4c21b0f6237676449c6baaa1039abae86b91636b6c91a7f8e61c87f89540"}, + {file = "aiohttp-3.13.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:65d2ccb7eabee90ce0503c17716fc77226be026dcc3e65cce859a30db715025b"}, + {file = "aiohttp-3.13.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b179331a481cb5529fca8b432d8d3c7001cb217513c94cd72d668d1248688a3"}, + {file = "aiohttp-3.13.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d4c940f02f49483b18b079d1c27ab948721852b281f8b015c058100e9421dd1"}, + {file = "aiohttp-3.13.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f9444f105664c4ce47a2a7171a2418bce5b7bae45fb610f4e2c36045d85911d3"}, + {file = "aiohttp-3.13.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:694976222c711d1d00ba131904beb60534f93966562f64440d0c9d41b8cdb440"}, + {file = "aiohttp-3.13.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f33ed1a2bf1997a36661874b017f5c4b760f41266341af36febaf271d179f6d7"}, + {file = "aiohttp-3.13.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e636b3c5f61da31a92bf0d91da83e58fdfa96f178ba682f11d24f31944cdd28c"}, + {file = "aiohttp-3.13.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5d2d94f1f5fcbe40838ac51a6ab5704a6f9ea42e72ceda48de5e6b898521da51"}, + {file = "aiohttp-3.13.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2be0e9ccf23e8a94f6f0650ce06042cefc6ac703d0d7ab6c7a917289f2539ad4"}, + {file = "aiohttp-3.13.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9af5e68ee47d6534d36791bbe9b646d2a7c7deb6fc24d7943628edfbb3581f29"}, + {file = "aiohttp-3.13.3-cp311-cp311-win32.whl", hash = "sha256:a2212ad43c0833a873d0fb3c63fa1bacedd4cf6af2fee62bf4b739ceec3ab239"}, + {file = "aiohttp-3.13.3-cp311-cp311-win_amd64.whl", hash = "sha256:642f752c3eb117b105acbd87e2c143de710987e09860d674e068c4c2c441034f"}, + {file = "aiohttp-3.13.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b903a4dfee7d347e2d87697d0713be59e0b87925be030c9178c5faa58ea58d5c"}, + {file = "aiohttp-3.13.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a45530014d7a1e09f4a55f4f43097ba0fd155089372e105e4bff4ca76cb1b168"}, + {file = "aiohttp-3.13.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27234ef6d85c914f9efeb77ff616dbf4ad2380be0cda40b4db086ffc7ddd1b7d"}, + {file = "aiohttp-3.13.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d32764c6c9aafb7fb55366a224756387cd50bfa720f32b88e0e6fa45b27dcf29"}, + {file = "aiohttp-3.13.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b1a6102b4d3ebc07dad44fbf07b45bb600300f15b552ddf1851b5390202ea2e3"}, + {file = "aiohttp-3.13.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c014c7ea7fb775dd015b2d3137378b7be0249a448a1612268b5a90c2d81de04d"}, + {file = "aiohttp-3.13.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2b8d8ddba8f95ba17582226f80e2de99c7a7948e66490ef8d947e272a93e9463"}, + {file = "aiohttp-3.13.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ae8dd55c8e6c4257eae3a20fd2c8f41edaea5992ed67156642493b8daf3cecc"}, + {file = "aiohttp-3.13.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:01ad2529d4b5035578f5081606a465f3b814c542882804e2e8cda61adf5c71bf"}, + {file = "aiohttp-3.13.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bb4f7475e359992b580559e008c598091c45b5088f28614e855e42d39c2f1033"}, + {file = "aiohttp-3.13.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c19b90316ad3b24c69cd78d5c9b4f3aa4497643685901185b65166293d36a00f"}, + {file = "aiohttp-3.13.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:96d604498a7c782cb15a51c406acaea70d8c027ee6b90c569baa6e7b93073679"}, + {file = "aiohttp-3.13.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:084911a532763e9d3dd95adf78a78f4096cd5f58cdc18e6fdbc1b58417a45423"}, + {file = "aiohttp-3.13.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7a4a94eb787e606d0a09404b9c38c113d3b099d508021faa615d70a0131907ce"}, + {file = "aiohttp-3.13.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87797e645d9d8e222e04160ee32aa06bc5c163e8499f24db719e7852ec23093a"}, + {file = "aiohttp-3.13.3-cp312-cp312-win32.whl", hash = "sha256:b04be762396457bef43f3597c991e192ee7da460a4953d7e647ee4b1c28e7046"}, + {file = "aiohttp-3.13.3-cp312-cp312-win_amd64.whl", hash = "sha256:e3531d63d3bdfa7e3ac5e9b27b2dd7ec9df3206a98e0b3445fa906f233264c57"}, + {file = "aiohttp-3.13.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5dff64413671b0d3e7d5918ea490bdccb97a4ad29b3f311ed423200b2203e01c"}, + {file = "aiohttp-3.13.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:87b9aab6d6ed88235aa2970294f496ff1a1f9adcd724d800e9b952395a80ffd9"}, + {file = "aiohttp-3.13.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:425c126c0dc43861e22cb1c14ba4c8e45d09516d0a3ae0a3f7494b79f5f233a3"}, + {file = "aiohttp-3.13.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f9120f7093c2a32d9647abcaf21e6ad275b4fbec5b55969f978b1a97c7c86bf"}, + {file = "aiohttp-3.13.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:697753042d57f4bf7122cab985bf15d0cef23c770864580f5af4f52023a56bd6"}, + {file = "aiohttp-3.13.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6de499a1a44e7de70735d0b39f67c8f25eb3d91eb3103be99ca0fa882cdd987d"}, + {file = "aiohttp-3.13.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:37239e9f9a7ea9ac5bf6b92b0260b01f8a22281996da609206a84df860bc1261"}, + {file = "aiohttp-3.13.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f76c1e3fe7d7c8afad7ed193f89a292e1999608170dcc9751a7462a87dfd5bc0"}, + {file = "aiohttp-3.13.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fc290605db2a917f6e81b0e1e0796469871f5af381ce15c604a3c5c7e51cb730"}, + {file = "aiohttp-3.13.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4021b51936308aeea0367b8f006dc999ca02bc118a0cc78c303f50a2ff6afb91"}, + {file = "aiohttp-3.13.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:49a03727c1bba9a97d3e93c9f93ca03a57300f484b6e935463099841261195d3"}, + {file = "aiohttp-3.13.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3d9908a48eb7416dc1f4524e69f1d32e5d90e3981e4e37eb0aa1cd18f9cfa2a4"}, + {file = "aiohttp-3.13.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2712039939ec963c237286113c68dbad80a82a4281543f3abf766d9d73228998"}, + {file = "aiohttp-3.13.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7bfdc049127717581866fa4708791220970ce291c23e28ccf3922c700740fdc0"}, + {file = "aiohttp-3.13.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8057c98e0c8472d8846b9c79f56766bcc57e3e8ac7bfd510482332366c56c591"}, + {file = "aiohttp-3.13.3-cp313-cp313-win32.whl", hash = "sha256:1449ceddcdbcf2e0446957863af03ebaaa03f94c090f945411b61269e2cb5daf"}, + {file = "aiohttp-3.13.3-cp313-cp313-win_amd64.whl", hash = "sha256:693781c45a4033d31d4187d2436f5ac701e7bbfe5df40d917736108c1cc7436e"}, + {file = "aiohttp-3.13.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:ea37047c6b367fd4bd632bff8077449b8fa034b69e812a18e0132a00fae6e808"}, + {file = "aiohttp-3.13.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6fc0e2337d1a4c3e6acafda6a78a39d4c14caea625124817420abceed36e2415"}, + {file = "aiohttp-3.13.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c685f2d80bb67ca8c3837823ad76196b3694b0159d232206d1e461d3d434666f"}, + {file = "aiohttp-3.13.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e377758516d262bde50c2584fc6c578af272559c409eecbdd2bae1601184d6"}, + {file = "aiohttp-3.13.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:34749271508078b261c4abb1767d42b8d0c0cc9449c73a4df494777dc55f0687"}, + {file = "aiohttp-3.13.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:82611aeec80eb144416956ec85b6ca45a64d76429c1ed46ae1b5f86c6e0c9a26"}, + {file = "aiohttp-3.13.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2fff83cfc93f18f215896e3a190e8e5cb413ce01553901aca925176e7568963a"}, + {file = "aiohttp-3.13.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bbe7d4cecacb439e2e2a8a1a7b935c25b812af7a5fd26503a66dadf428e79ec1"}, + {file = "aiohttp-3.13.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b928f30fe49574253644b1ca44b1b8adbd903aa0da4b9054a6c20fc7f4092a25"}, + {file = "aiohttp-3.13.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7b5e8fe4de30df199155baaf64f2fcd604f4c678ed20910db8e2c66dc4b11603"}, + {file = "aiohttp-3.13.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:8542f41a62bcc58fc7f11cf7c90e0ec324ce44950003feb70640fc2a9092c32a"}, + {file = "aiohttp-3.13.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5e1d8c8b8f1d91cd08d8f4a3c2b067bfca6ec043d3ff36de0f3a715feeedf926"}, + {file = "aiohttp-3.13.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:90455115e5da1c3c51ab619ac57f877da8fd6d73c05aacd125c5ae9819582aba"}, + {file = "aiohttp-3.13.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:042e9e0bcb5fba81886c8b4fbb9a09d6b8a00245fd8d88e4d989c1f96c74164c"}, + {file = "aiohttp-3.13.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2eb752b102b12a76ca02dff751a801f028b4ffbbc478840b473597fc91a9ed43"}, + {file = "aiohttp-3.13.3-cp314-cp314-win32.whl", hash = "sha256:b556c85915d8efaed322bf1bdae9486aa0f3f764195a0fb6ee962e5c71ef5ce1"}, + {file = "aiohttp-3.13.3-cp314-cp314-win_amd64.whl", hash = "sha256:9bf9f7a65e7aa20dd764151fb3d616c81088f91f8df39c3893a536e279b4b984"}, + {file = "aiohttp-3.13.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:05861afbbec40650d8a07ea324367cb93e9e8cc7762e04dd4405df99fa65159c"}, + {file = "aiohttp-3.13.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2fc82186fadc4a8316768d61f3722c230e2c1dcab4200d52d2ebdf2482e47592"}, + {file = "aiohttp-3.13.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0add0900ff220d1d5c5ebbf99ed88b0c1bbf87aa7e4262300ed1376a6b13414f"}, + {file = "aiohttp-3.13.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:568f416a4072fbfae453dcf9a99194bbb8bdeab718e08ee13dfa2ba0e4bebf29"}, + {file = "aiohttp-3.13.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:add1da70de90a2569c5e15249ff76a631ccacfe198375eead4aadf3b8dc849dc"}, + {file = "aiohttp-3.13.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b47b7ba335d2e9b1239fa571131a87e2d8ec96b333e68b2a305e7a98b0bae2"}, + {file = "aiohttp-3.13.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dd4dce1c718e38081c8f35f323209d4c1df7d4db4bab1b5c88a6b4d12b74587"}, + {file = "aiohttp-3.13.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34bac00a67a812570d4a460447e1e9e06fae622946955f939051e7cc895cfab8"}, + {file = "aiohttp-3.13.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a19884d2ee70b06d9204b2727a7b9f983d0c684c650254679e716b0b77920632"}, + {file = "aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ca7f2bb6ba8348a3614c7918cc4bb73268c5ac2a207576b7afea19d3d9f64"}, + {file = "aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:b0d95340658b9d2f11d9697f59b3814a9d3bb4b7a7c20b131df4bcef464037c0"}, + {file = "aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1e53262fd202e4b40b70c3aff944a8155059beedc8a89bba9dc1f9ef06a1b56"}, + {file = "aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d60ac9663f44168038586cab2157e122e46bdef09e9368b37f2d82d354c23f72"}, + {file = "aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:90751b8eed69435bac9ff4e3d2f6b3af1f57e37ecb0fbeee59c0174c9e2d41df"}, + {file = "aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fc353029f176fd2b3ec6cfc71be166aba1936fe5d73dd1992ce289ca6647a9aa"}, + {file = "aiohttp-3.13.3-cp314-cp314t-win32.whl", hash = "sha256:2e41b18a58da1e474a057b3d35248d8320029f61d70a37629535b16a0c8f3767"}, + {file = "aiohttp-3.13.3-cp314-cp314t-win_amd64.whl", hash = "sha256:44531a36aa2264a1860089ffd4dce7baf875ee5a6079d5fb42e261c704ef7344"}, + {file = "aiohttp-3.13.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:31a83ea4aead760dfcb6962efb1d861db48c34379f2ff72db9ddddd4cda9ea2e"}, + {file = "aiohttp-3.13.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:988a8c5e317544fdf0d39871559e67b6341065b87fceac641108c2096d5506b7"}, + {file = "aiohttp-3.13.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9b174f267b5cfb9a7dba9ee6859cecd234e9a681841eb85068059bc867fb8f02"}, + {file = "aiohttp-3.13.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:947c26539750deeaee933b000fb6517cc770bbd064bad6033f1cff4803881e43"}, + {file = "aiohttp-3.13.3-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9ebf57d09e131f5323464bd347135a88622d1c0976e88ce15b670e7ad57e4bd6"}, + {file = "aiohttp-3.13.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4ae5b5a0e1926e504c81c5b84353e7a5516d8778fbbff00429fe7b05bb25cbce"}, + {file = "aiohttp-3.13.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2ba0eea45eb5cc3172dbfc497c066f19c41bac70963ea1a67d51fc92e4cf9a80"}, + {file = "aiohttp-3.13.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bae5c2ed2eae26cc382020edad80d01f36cb8e746da40b292e68fec40421dc6a"}, + {file = "aiohttp-3.13.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8a60e60746623925eab7d25823329941aee7242d559baa119ca2b253c88a7bd6"}, + {file = "aiohttp-3.13.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e50a2e1404f063427c9d027378472316201a2290959a295169bcf25992d04558"}, + {file = "aiohttp-3.13.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:9a9dc347e5a3dc7dfdbc1f82da0ef29e388ddb2ed281bfce9dd8248a313e62b7"}, + {file = "aiohttp-3.13.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b46020d11d23fe16551466c77823df9cc2f2c1e63cc965daf67fa5eec6ca1877"}, + {file = "aiohttp-3.13.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:69c56fbc1993fa17043e24a546959c0178fe2b5782405ad4559e6c13975c15e3"}, + {file = "aiohttp-3.13.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:b99281b0704c103d4e11e72a76f1b543d4946fea7dd10767e7e1b5f00d4e5704"}, + {file = "aiohttp-3.13.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:40c5e40ecc29ba010656c18052b877a1c28f84344825efa106705e835c28530f"}, + {file = "aiohttp-3.13.3-cp39-cp39-win32.whl", hash = "sha256:56339a36b9f1fc708260c76c87e593e2afb30d26de9ae1eb445b5e051b98a7a1"}, + {file = "aiohttp-3.13.3-cp39-cp39-win_amd64.whl", hash = "sha256:c6b8568a3bb5819a0ad087f16d40e5a3fb6099f39ea1d5625a3edc1e923fc538"}, + {file = "aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88"}, ] [package.dependencies] @@ -119,7 +153,7 @@ propcache = ">=0.2.0" yarl = ">=1.17.0,<2.0" [package.extras] -speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.3.0)", "brotlicffi ; platform_python_implementation != \"CPython\""] +speedups = ["Brotli (>=1.2) ; platform_python_implementation == \"CPython\"", "aiodns (>=3.3.0)", "backports.zstd ; platform_python_implementation == \"CPython\" and python_version < \"3.14\"", "brotlicffi (>=1.2) ; platform_python_implementation != \"CPython\""] [[package]] name = "aiosignal" @@ -152,24 +186,16 @@ files = [ [[package]] name = "attrs" -version = "25.3.0" +version = "25.4.0" description = "Classes Without Boilerplate" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, - {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, + {file = "attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373"}, + {file = "attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11"}, ] -[package.extras] -benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] -tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] - [[package]] name = "backports-tarfile" version = "1.2.0" @@ -177,7 +203,7 @@ description = "Backport of CPython tarfile module" optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\"" +markers = "python_version < \"3.12\" and platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" files = [ {file = "backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34"}, {file = "backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991"}, @@ -241,197 +267,235 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" -version = "2025.6.15" +version = "2026.1.4" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.7" groups = ["main", "dev"] files = [ - {file = "certifi-2025.6.15-py3-none-any.whl", hash = "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057"}, - {file = "certifi-2025.6.15.tar.gz", hash = "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b"}, + {file = "certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c"}, + {file = "certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120"}, ] [[package]] name = "cffi" -version = "1.17.1" +version = "2.0.0" description = "Foreign Function Interface for Python calling C code." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\"" -files = [ - {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, - {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, - {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, - {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, - {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, - {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, - {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, - {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, - {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, - {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, - {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, - {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, - {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, - {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, - {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, - {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, - {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, - {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, - {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, - {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, - {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, - {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, - {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, - {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, - {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, - {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, - {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, - {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, +markers = "platform_python_implementation != \"PyPy\" and platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" +files = [ + {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, + {file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb"}, + {file = "cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a"}, + {file = "cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743"}, + {file = "cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5"}, + {file = "cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5"}, + {file = "cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187"}, + {file = "cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18"}, + {file = "cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5"}, + {file = "cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b"}, + {file = "cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27"}, + {file = "cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75"}, + {file = "cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1"}, + {file = "cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f"}, + {file = "cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25"}, + {file = "cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4"}, + {file = "cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e"}, + {file = "cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6"}, + {file = "cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322"}, + {file = "cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a"}, + {file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"}, + {file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"}, ] [package.dependencies] -pycparser = "*" +pycparser = {version = "*", markers = "implementation_name != \"PyPy\""} [[package]] name = "charset-normalizer" -version = "3.4.2" +version = "3.4.4" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" groups = ["main", "dev"] files = [ - {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cad5f45b3146325bb38d6855642f6fd609c3f7cad4dbaf75549bf3b904d3184"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2680962a4848b3c4f155dc2ee64505a9c57186d0d56b43123b17ca3de18f0fa"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:36b31da18b8890a76ec181c3cf44326bf2c48e36d393ca1b72b3f484113ea344"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4074c5a429281bf056ddd4c5d3b740ebca4d43ffffe2ef4bf4d2d05114299da"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9e36a97bee9b86ef9a1cf7bb96747eb7a15c2f22bdb5b516434b00f2a599f02"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:1b1bde144d98e446b056ef98e59c256e9294f6b74d7af6846bf5ffdafd687a7d"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:915f3849a011c1f593ab99092f3cecfcb4d65d8feb4a64cf1bf2d22074dc0ec4"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:fb707f3e15060adf5b7ada797624a6c6e0138e2a26baa089df64c68ee98e040f"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:25a23ea5c7edc53e0f29bae2c44fcb5a1aa10591aae107f2a2b2583a9c5cbc64"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:770cab594ecf99ae64c236bc9ee3439c3f46be49796e265ce0cc8bc17b10294f"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-win32.whl", hash = "sha256:6a0289e4589e8bdfef02a80478f1dfcb14f0ab696b5a00e1f4b8a14a307a3c58"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6fc1f5b51fa4cecaa18f2bd7a003f3dd039dd615cd69a2afd6d3b19aed6775f2"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-win32.whl", hash = "sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e"}, - {file = "charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0"}, - {file = "charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-win32.whl", hash = "sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-win32.whl", hash = "sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-win_arm64.whl", hash = "sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50"}, + {file = "charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f"}, + {file = "charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a"}, ] [[package]] @@ -441,6 +505,7 @@ description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" groups = ["dev"] +markers = "python_full_version < \"3.14.0\" or platform_python_implementation == \"PyPy\"" files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -449,6 +514,22 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} +[[package]] +name = "click" +version = "8.3.1" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +markers = "python_version >= \"3.14\" and platform_python_implementation != \"PyPy\"" +files = [ + {file = "click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6"}, + {file = "click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + [[package]] name = "colorama" version = "0.4.6" @@ -469,7 +550,7 @@ description = "cryptography is a package which provides cryptographic recipes an optional = false python-versions = ">=3.7" groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" +markers = "(python_full_version < \"3.14.0\" or platform_python_implementation == \"PyPy\") and platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" files = [ {file = "cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e"}, {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e"}, @@ -513,29 +594,107 @@ ssh = ["bcrypt (>=3.1.5)"] test = ["certifi", "cryptography-vectors (==43.0.3)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] +[[package]] +name = "cryptography" +version = "46.0.3" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = "!=3.9.0,!=3.9.1,>=3.8" +groups = ["dev"] +markers = "python_version >= \"3.14\" and platform_python_implementation != \"PyPy\" and platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" +files = [ + {file = "cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a"}, + {file = "cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc"}, + {file = "cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d"}, + {file = "cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb"}, + {file = "cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849"}, + {file = "cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8"}, + {file = "cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec"}, + {file = "cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91"}, + {file = "cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e"}, + {file = "cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926"}, + {file = "cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71"}, + {file = "cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac"}, + {file = "cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018"}, + {file = "cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb"}, + {file = "cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c"}, + {file = "cryptography-46.0.3-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:00a5e7e87938e5ff9ff5447ab086a5706a957137e6e433841e9d24f38a065217"}, + {file = "cryptography-46.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8daeb2d2174beb4575b77482320303f3d39b8e81153da4f0fb08eb5fe86a6c5"}, + {file = "cryptography-46.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39b6755623145ad5eff1dab323f4eae2a32a77a7abef2c5089a04a3d04366715"}, + {file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:db391fa7c66df6762ee3f00c95a89e6d428f4d60e7abc8328f4fe155b5ac6e54"}, + {file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:78a97cf6a8839a48c49271cdcbd5cf37ca2c1d6b7fdd86cc864f302b5e9bf459"}, + {file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:dfb781ff7eaa91a6f7fd41776ec37c5853c795d3b358d4896fdbb5df168af422"}, + {file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6f61efb26e76c45c4a227835ddeae96d83624fb0d29eb5df5b96e14ed1a0afb7"}, + {file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:23b1a8f26e43f47ceb6d6a43115f33a5a37d57df4ea0ca295b780ae8546e8044"}, + {file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b419ae593c86b87014b9be7396b385491ad7f320bde96826d0dd174459e54665"}, + {file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:50fc3343ac490c6b08c0cf0d704e881d0d660be923fd3076db3e932007e726e3"}, + {file = "cryptography-46.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22d7e97932f511d6b0b04f2bfd818d73dcd5928db509460aaf48384778eb6d20"}, + {file = "cryptography-46.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d55f3dffadd674514ad19451161118fd010988540cee43d8bc20675e775925de"}, + {file = "cryptography-46.0.3-cp314-cp314t-win32.whl", hash = "sha256:8a6e050cb6164d3f830453754094c086ff2d0b2f3a897a1d9820f6139a1f0914"}, + {file = "cryptography-46.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:760f83faa07f8b64e9c33fc963d790a2edb24efb479e3520c14a45741cd9b2db"}, + {file = "cryptography-46.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:516ea134e703e9fe26bcd1277a4b59ad30586ea90c365a87781d7887a646fe21"}, + {file = "cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936"}, + {file = "cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683"}, + {file = "cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d"}, + {file = "cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0"}, + {file = "cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc"}, + {file = "cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3"}, + {file = "cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971"}, + {file = "cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac"}, + {file = "cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04"}, + {file = "cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506"}, + {file = "cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963"}, + {file = "cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4"}, + {file = "cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df"}, + {file = "cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f"}, + {file = "cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372"}, + {file = "cryptography-46.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a23582810fedb8c0bc47524558fb6c56aac3fc252cb306072fd2815da2a47c32"}, + {file = "cryptography-46.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e7aec276d68421f9574040c26e2a7c3771060bc0cff408bae1dcb19d3ab1e63c"}, + {file = "cryptography-46.0.3-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7ce938a99998ed3c8aa7e7272dca1a610401ede816d36d0693907d863b10d9ea"}, + {file = "cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:191bb60a7be5e6f54e30ba16fdfae78ad3a342a0599eb4193ba88e3f3d6e185b"}, + {file = "cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c70cc23f12726be8f8bc72e41d5065d77e4515efae3690326764ea1b07845cfb"}, + {file = "cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:9394673a9f4de09e28b5356e7fff97d778f8abad85c9d5ac4a4b7e25a0de7717"}, + {file = "cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:94cd0549accc38d1494e1f8de71eca837d0509d0d44bf11d158524b0e12cebf9"}, + {file = "cryptography-46.0.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6b5063083824e5509fdba180721d55909ffacccc8adbec85268b48439423d78c"}, + {file = "cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1"}, +] + +[package.dependencies] +cffi = {version = ">=2.0.0", markers = "python_full_version >= \"3.9.0\" and platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-inline-tabs", "sphinx-rtd-theme (>=3.0.0)"] +docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"] +nox = ["nox[uv] (>=2024.4.15)"] +pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.14)", "ruff (>=0.11.11)"] +sdist = ["build (>=1.0.0)"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi (>=2024)", "cryptography-vectors (==46.0.3)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] +test-randomorder = ["pytest-randomly"] + [[package]] name = "docutils" -version = "0.21.2" +version = "0.22.4" description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, - {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, + {file = "docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de"}, + {file = "docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968"}, ] [[package]] name = "exceptiongroup" -version = "1.3.0" +version = "1.3.1" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" groups = ["dev"] markers = "python_version < \"3.11\"" files = [ - {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, - {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, + {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, + {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, ] [package.dependencies] @@ -563,116 +722,142 @@ pyflakes = ">=3.4.0,<3.5.0" [[package]] name = "frozenlist" -version = "1.7.0" +version = "1.8.0" description = "A list-like structure which implements collections.abc.MutableSequence" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "frozenlist-1.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cc4df77d638aa2ed703b878dd093725b72a824c3c546c076e8fdf276f78ee84a"}, - {file = "frozenlist-1.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:716a9973a2cc963160394f701964fe25012600f3d311f60c790400b00e568b61"}, - {file = "frozenlist-1.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0fd1bad056a3600047fb9462cff4c5322cebc59ebf5d0a3725e0ee78955001d"}, - {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3789ebc19cb811163e70fe2bd354cea097254ce6e707ae42e56f45e31e96cb8e"}, - {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af369aa35ee34f132fcfad5be45fbfcde0e3a5f6a1ec0712857f286b7d20cca9"}, - {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac64b6478722eeb7a3313d494f8342ef3478dff539d17002f849101b212ef97c"}, - {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f89f65d85774f1797239693cef07ad4c97fdd0639544bad9ac4b869782eb1981"}, - {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1073557c941395fdfcfac13eb2456cb8aad89f9de27bae29fabca8e563b12615"}, - {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed8d2fa095aae4bdc7fdd80351009a48d286635edffee66bf865e37a9125c50"}, - {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:24c34bea555fe42d9f928ba0a740c553088500377448febecaa82cc3e88aa1fa"}, - {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:69cac419ac6a6baad202c85aaf467b65ac860ac2e7f2ac1686dc40dbb52f6577"}, - {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:960d67d0611f4c87da7e2ae2eacf7ea81a5be967861e0c63cf205215afbfac59"}, - {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:41be2964bd4b15bf575e5daee5a5ce7ed3115320fb3c2b71fca05582ffa4dc9e"}, - {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:46d84d49e00c9429238a7ce02dc0be8f6d7cd0cd405abd1bebdc991bf27c15bd"}, - {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:15900082e886edb37480335d9d518cec978afc69ccbc30bd18610b7c1b22a718"}, - {file = "frozenlist-1.7.0-cp310-cp310-win32.whl", hash = "sha256:400ddd24ab4e55014bba442d917203c73b2846391dd42ca5e38ff52bb18c3c5e"}, - {file = "frozenlist-1.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:6eb93efb8101ef39d32d50bce242c84bcbddb4f7e9febfa7b524532a239b4464"}, - {file = "frozenlist-1.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aa51e147a66b2d74de1e6e2cf5921890de6b0f4820b257465101d7f37b49fb5a"}, - {file = "frozenlist-1.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9b35db7ce1cd71d36ba24f80f0c9e7cff73a28d7a74e91fe83e23d27c7828750"}, - {file = "frozenlist-1.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34a69a85e34ff37791e94542065c8416c1afbf820b68f720452f636d5fb990cd"}, - {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a646531fa8d82c87fe4bb2e596f23173caec9185bfbca5d583b4ccfb95183e2"}, - {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:79b2ffbba483f4ed36a0f236ccb85fbb16e670c9238313709638167670ba235f"}, - {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a26f205c9ca5829cbf82bb2a84b5c36f7184c4316617d7ef1b271a56720d6b30"}, - {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bcacfad3185a623fa11ea0e0634aac7b691aa925d50a440f39b458e41c561d98"}, - {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72c1b0fe8fe451b34f12dce46445ddf14bd2a5bcad7e324987194dc8e3a74c86"}, - {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61d1a5baeaac6c0798ff6edfaeaa00e0e412d49946c53fae8d4b8e8b3566c4ae"}, - {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7edf5c043c062462f09b6820de9854bf28cc6cc5b6714b383149745e287181a8"}, - {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d50ac7627b3a1bd2dcef6f9da89a772694ec04d9a61b66cf87f7d9446b4a0c31"}, - {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce48b2fece5aeb45265bb7a58259f45027db0abff478e3077e12b05b17fb9da7"}, - {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:fe2365ae915a1fafd982c146754e1de6ab3478def8a59c86e1f7242d794f97d5"}, - {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:45a6f2fdbd10e074e8814eb98b05292f27bad7d1883afbe009d96abdcf3bc898"}, - {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:21884e23cffabb157a9dd7e353779077bf5b8f9a58e9b262c6caad2ef5f80a56"}, - {file = "frozenlist-1.7.0-cp311-cp311-win32.whl", hash = "sha256:284d233a8953d7b24f9159b8a3496fc1ddc00f4db99c324bd5fb5f22d8698ea7"}, - {file = "frozenlist-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:387cbfdcde2f2353f19c2f66bbb52406d06ed77519ac7ee21be0232147c2592d"}, - {file = "frozenlist-1.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3dbf9952c4bb0e90e98aec1bd992b3318685005702656bc6f67c1a32b76787f2"}, - {file = "frozenlist-1.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1f5906d3359300b8a9bb194239491122e6cf1444c2efb88865426f170c262cdb"}, - {file = "frozenlist-1.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3dabd5a8f84573c8d10d8859a50ea2dec01eea372031929871368c09fa103478"}, - {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa57daa5917f1738064f302bf2626281a1cb01920c32f711fbc7bc36111058a8"}, - {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c193dda2b6d49f4c4398962810fa7d7c78f032bf45572b3e04dd5249dff27e08"}, - {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe2b675cf0aaa6d61bf8fbffd3c274b3c9b7b1623beb3809df8a81399a4a9c4"}, - {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8fc5d5cda37f62b262405cf9652cf0856839c4be8ee41be0afe8858f17f4c94b"}, - {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d5ce521d1dd7d620198829b87ea002956e4319002ef0bc8d3e6d045cb4646e"}, - {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:488d0a7d6a0008ca0db273c542098a0fa9e7dfaa7e57f70acef43f32b3f69dca"}, - {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:15a7eaba63983d22c54d255b854e8108e7e5f3e89f647fc854bd77a237e767df"}, - {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1eaa7e9c6d15df825bf255649e05bd8a74b04a4d2baa1ae46d9c2d00b2ca2cb5"}, - {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4389e06714cfa9d47ab87f784a7c5be91d3934cd6e9a7b85beef808297cc025"}, - {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:73bd45e1488c40b63fe5a7df892baf9e2a4d4bb6409a2b3b78ac1c6236178e01"}, - {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99886d98e1643269760e5fe0df31e5ae7050788dd288947f7f007209b8c33f08"}, - {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:290a172aae5a4c278c6da8a96222e6337744cd9c77313efe33d5670b9f65fc43"}, - {file = "frozenlist-1.7.0-cp312-cp312-win32.whl", hash = "sha256:426c7bc70e07cfebc178bc4c2bf2d861d720c4fff172181eeb4a4c41d4ca2ad3"}, - {file = "frozenlist-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:563b72efe5da92e02eb68c59cb37205457c977aa7a449ed1b37e6939e5c47c6a"}, - {file = "frozenlist-1.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee80eeda5e2a4e660651370ebffd1286542b67e268aa1ac8d6dbe973120ef7ee"}, - {file = "frozenlist-1.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d1a81c85417b914139e3a9b995d4a1c84559afc839a93cf2cb7f15e6e5f6ed2d"}, - {file = "frozenlist-1.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cbb65198a9132ebc334f237d7b0df163e4de83fb4f2bdfe46c1e654bdb0c5d43"}, - {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dab46c723eeb2c255a64f9dc05b8dd601fde66d6b19cdb82b2e09cc6ff8d8b5d"}, - {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6aeac207a759d0dedd2e40745575ae32ab30926ff4fa49b1635def65806fddee"}, - {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bd8c4e58ad14b4fa7802b8be49d47993182fdd4023393899632c88fd8cd994eb"}, - {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04fb24d104f425da3540ed83cbfc31388a586a7696142004c577fa61c6298c3f"}, - {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a5c505156368e4ea6b53b5ac23c92d7edc864537ff911d2fb24c140bb175e60"}, - {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bd7eb96a675f18aa5c553eb7ddc24a43c8c18f22e1f9925528128c052cdbe00"}, - {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:05579bf020096fe05a764f1f84cd104a12f78eaab68842d036772dc6d4870b4b"}, - {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:376b6222d114e97eeec13d46c486facd41d4f43bab626b7c3f6a8b4e81a5192c"}, - {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0aa7e176ebe115379b5b1c95b4096fb1c17cce0847402e227e712c27bdb5a949"}, - {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3fbba20e662b9c2130dc771e332a99eff5da078b2b2648153a40669a6d0e36ca"}, - {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f3f4410a0a601d349dd406b5713fec59b4cee7e71678d5b17edda7f4655a940b"}, - {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2cdfaaec6a2f9327bf43c933c0319a7c429058e8537c508964a133dffee412e"}, - {file = "frozenlist-1.7.0-cp313-cp313-win32.whl", hash = "sha256:5fc4df05a6591c7768459caba1b342d9ec23fa16195e744939ba5914596ae3e1"}, - {file = "frozenlist-1.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:52109052b9791a3e6b5d1b65f4b909703984b770694d3eb64fad124c835d7cba"}, - {file = "frozenlist-1.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a6f86e4193bb0e235ef6ce3dde5cbabed887e0b11f516ce8a0f4d3b33078ec2d"}, - {file = "frozenlist-1.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:82d664628865abeb32d90ae497fb93df398a69bb3434463d172b80fc25b0dd7d"}, - {file = "frozenlist-1.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:912a7e8375a1c9a68325a902f3953191b7b292aa3c3fb0d71a216221deca460b"}, - {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9537c2777167488d539bc5de2ad262efc44388230e5118868e172dd4a552b146"}, - {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f34560fb1b4c3e30ba35fa9a13894ba39e5acfc5f60f57d8accde65f46cc5e74"}, - {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:acd03d224b0175f5a850edc104ac19040d35419eddad04e7cf2d5986d98427f1"}, - {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2038310bc582f3d6a09b3816ab01737d60bf7b1ec70f5356b09e84fb7408ab1"}, - {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c05e4c8e5f36e5e088caa1bf78a687528f83c043706640a92cb76cd6999384"}, - {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:765bb588c86e47d0b68f23c1bee323d4b703218037765dcf3f25c838c6fecceb"}, - {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:32dc2e08c67d86d0969714dd484fd60ff08ff81d1a1e40a77dd34a387e6ebc0c"}, - {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:c0303e597eb5a5321b4de9c68e9845ac8f290d2ab3f3e2c864437d3c5a30cd65"}, - {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:a47f2abb4e29b3a8d0b530f7c3598badc6b134562b1a5caee867f7c62fee51e3"}, - {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:3d688126c242a6fabbd92e02633414d40f50bb6002fa4cf995a1d18051525657"}, - {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:4e7e9652b3d367c7bd449a727dc79d5043f48b88d0cbfd4f9f1060cf2b414104"}, - {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1a85e345b4c43db8b842cab1feb41be5cc0b10a1830e6295b69d7310f99becaf"}, - {file = "frozenlist-1.7.0-cp313-cp313t-win32.whl", hash = "sha256:3a14027124ddb70dfcee5148979998066897e79f89f64b13328595c4bdf77c81"}, - {file = "frozenlist-1.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3bf8010d71d4507775f658e9823210b7427be36625b387221642725b515dcf3e"}, - {file = "frozenlist-1.7.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cea3dbd15aea1341ea2de490574a4a37ca080b2ae24e4b4f4b51b9057b4c3630"}, - {file = "frozenlist-1.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7d536ee086b23fecc36c2073c371572374ff50ef4db515e4e503925361c24f71"}, - {file = "frozenlist-1.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dfcebf56f703cb2e346315431699f00db126d158455e513bd14089d992101e44"}, - {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:974c5336e61d6e7eb1ea5b929cb645e882aadab0095c5a6974a111e6479f8878"}, - {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c70db4a0ab5ab20878432c40563573229a7ed9241506181bba12f6b7d0dc41cb"}, - {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1137b78384eebaf70560a36b7b229f752fb64d463d38d1304939984d5cb887b6"}, - {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e793a9f01b3e8b5c0bc646fb59140ce0efcc580d22a3468d70766091beb81b35"}, - {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74739ba8e4e38221d2c5c03d90a7e542cb8ad681915f4ca8f68d04f810ee0a87"}, - {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e63344c4e929b1a01e29bc184bbb5fd82954869033765bfe8d65d09e336a677"}, - {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2ea2a7369eb76de2217a842f22087913cdf75f63cf1307b9024ab82dfb525938"}, - {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:836b42f472a0e006e02499cef9352ce8097f33df43baaba3e0a28a964c26c7d2"}, - {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e22b9a99741294b2571667c07d9f8cceec07cb92aae5ccda39ea1b6052ed4319"}, - {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:9a19e85cc503d958abe5218953df722748d87172f71b73cf3c9257a91b999890"}, - {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f22dac33bb3ee8fe3e013aa7b91dc12f60d61d05b7fe32191ffa84c3aafe77bd"}, - {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9ccec739a99e4ccf664ea0775149f2749b8a6418eb5b8384b4dc0a7d15d304cb"}, - {file = "frozenlist-1.7.0-cp39-cp39-win32.whl", hash = "sha256:b3950f11058310008a87757f3eee16a8e1ca97979833239439586857bc25482e"}, - {file = "frozenlist-1.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:43a82fce6769c70f2f5a06248b614a7d268080a9d20f7457ef10ecee5af82b63"}, - {file = "frozenlist-1.7.0-py3-none-any.whl", hash = "sha256:9a5af342e34f7e97caf8c995864c7a396418ae2859cc6fdf1b1073020d516a7e"}, - {file = "frozenlist-1.7.0.tar.gz", hash = "sha256:2e310d81923c2437ea8670467121cc3e9b0f76d3043cc1d2331d56c7fb7a3a8f"}, + {file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011"}, + {file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565"}, + {file = "frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7"}, + {file = "frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a"}, + {file = "frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6"}, + {file = "frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967"}, + {file = "frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25"}, + {file = "frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b"}, + {file = "frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa"}, + {file = "frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf"}, + {file = "frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746"}, + {file = "frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed"}, + {file = "frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496"}, + {file = "frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231"}, + {file = "frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7"}, + {file = "frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806"}, + {file = "frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0"}, + {file = "frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d8b7138e5cd0647e4523d6685b0eac5d4be9a184ae9634492f25c6eb38c12a47"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a6483e309ca809f1efd154b4d37dc6d9f61037d6c6a81c2dc7a15cb22c8c5dca"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b9290cf81e95e93fdf90548ce9d3c1211cf574b8e3f4b3b7cb0537cf2227068"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:59a6a5876ca59d1b63af8cd5e7ffffb024c3dc1e9cf9301b21a2e76286505c95"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6dc4126390929823e2d2d9dc79ab4046ed74680360fc5f38b585c12c66cdf459"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:332db6b2563333c5671fecacd085141b5800cb866be16d5e3eb15a2086476675"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ff15928d62a0b80bb875655c39bf517938c7d589554cbd2669be42d97c2cb61"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7bf6cdf8e07c8151fba6fe85735441240ec7f619f935a5205953d58009aef8c6"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:48e6d3f4ec5c7273dfe83ff27c91083c6c9065af655dc2684d2c200c94308bb5"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:1a7607e17ad33361677adcd1443edf6f5da0ce5e5377b798fba20fae194825f3"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3a935c3a4e89c733303a2d5a7c257ea44af3a56c8202df486b7f5de40f37e1"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:940d4a017dbfed9daf46a3b086e1d2167e7012ee297fef9e1c545c4d022f5178"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b9be22a69a014bc47e78072d0ecae716f5eb56c15238acca0f43d6eb8e4a5bda"}, + {file = "frozenlist-1.8.0-cp39-cp39-win32.whl", hash = "sha256:1aa77cb5697069af47472e39612976ed05343ff2e84a3dcf15437b232cbfd087"}, + {file = "frozenlist-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:7398c222d1d405e796970320036b1b563892b65809d9e5261487bb2c7f7b5c6a"}, + {file = "frozenlist-1.8.0-cp39-cp39-win_arm64.whl", hash = "sha256:b4f3b365f31c6cd4af24545ca0a244a53688cad8834e32f56831c4923b50a103"}, + {file = "frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d"}, + {file = "frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad"}, ] [[package]] @@ -697,14 +882,14 @@ test = ["coverage[toml]", "pretend", "pytest", "pytest-cov"] [[package]] name = "idna" -version = "3.10" +version = "3.11" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" groups = ["main", "dev"] files = [ - {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, - {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, + {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, + {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, ] [package.extras] @@ -712,15 +897,15 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2 [[package]] name = "importlib-metadata" -version = "8.7.0" +version = "8.7.1" description = "Read metadata from Python packages" optional = false python-versions = ">=3.9" groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version == \"3.9\"" +markers = "python_version < \"3.12\" and platform_machine != \"ppc64le\" and platform_machine != \"s390x\" or python_version == \"3.9\"" files = [ - {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"}, - {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"}, + {file = "importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151"}, + {file = "importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb"}, ] [package.dependencies] @@ -730,10 +915,10 @@ zipp = ">=3.20" check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=2.2)"] +enabler = ["pytest-enabler (>=3.4)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] -type = ["pytest-mypy"] +test = ["flufl.flake8", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["mypy (<1.19) ; platform_python_implementation == \"PyPy\"", "pytest-mypy (>=1.0.1)"] [[package]] name = "iniconfig" @@ -742,11 +927,25 @@ description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.8" groups = ["dev"] +markers = "python_full_version < \"3.14.0\" or platform_python_implementation == \"PyPy\"" files = [ {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, ] +[[package]] +name = "iniconfig" +version = "2.3.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +markers = "python_version >= \"3.14\" and platform_python_implementation != \"PyPy\"" +files = [ + {file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"}, + {file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"}, +] + [[package]] name = "jaraco-classes" version = "3.4.0" @@ -769,47 +968,51 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-ena [[package]] name = "jaraco-context" -version = "6.0.1" +version = "6.0.2" description = "Useful decorators and context managers" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" files = [ - {file = "jaraco.context-6.0.1-py3-none-any.whl", hash = "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4"}, - {file = "jaraco_context-6.0.1.tar.gz", hash = "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3"}, + {file = "jaraco_context-6.0.2-py3-none-any.whl", hash = "sha256:55fc21af4b4f9ca94aa643b6ee7fe13b1e4c01abf3aeb98ca4ad9c80b741c786"}, + {file = "jaraco_context-6.0.2.tar.gz", hash = "sha256:953ae8dddb57b1d791bf72ea1009b32088840a7dd19b9ba16443f62be919ee57"}, ] [package.dependencies] "backports.tarfile" = {version = "*", markers = "python_version < \"3.12\""} [package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -test = ["portend", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +enabler = ["pytest-enabler (>=3.4)"] +test = ["jaraco.test (>=5.6.0)", "portend", "pytest (>=6,!=8.1.*)"] +type = ["mypy (<1.19) ; platform_python_implementation == \"PyPy\"", "pytest-mypy (>=1.0.1)"] [[package]] name = "jaraco-functools" -version = "4.1.0" +version = "4.4.0" description = "Functools like those found in stdlib" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" files = [ - {file = "jaraco.functools-4.1.0-py3-none-any.whl", hash = "sha256:ad159f13428bc4acbf5541ad6dec511f91573b90fba04df61dafa2a1231cf649"}, - {file = "jaraco_functools-4.1.0.tar.gz", hash = "sha256:70f7e0e2ae076498e212562325e805204fc092d7b4c17e0e86c959e249701a9d"}, + {file = "jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176"}, + {file = "jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb"}, ] [package.dependencies] -more-itertools = "*" +more_itertools = "*" [package.extras] check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=2.2)"] +enabler = ["pytest-enabler (>=3.4)"] test = ["jaraco.classes", "pytest (>=6,!=8.1.*)"] -type = ["pytest-mypy"] +type = ["mypy (<1.19) ; platform_python_implementation == \"PyPy\"", "pytest-mypy (>=1.0.1)"] [[package]] name = "jeepney" @@ -830,15 +1033,15 @@ trio = ["trio"] [[package]] name = "keyring" -version = "25.6.0" +version = "25.7.0" description = "Store and access your passwords safely." optional = false python-versions = ">=3.9" groups = ["dev"] markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" files = [ - {file = "keyring-25.6.0-py3-none-any.whl", hash = "sha256:552a3f7af126ece7ed5c89753650eec89c7eaae8617d0aa4d9ad2b75111266bd"}, - {file = "keyring-25.6.0.tar.gz", hash = "sha256:0b39998aa941431eb3d9b0d4b2460bc773b9df6fed7621c2dfb291a7e0187a66"}, + {file = "keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f"}, + {file = "keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b"}, ] [package.dependencies] @@ -855,9 +1058,9 @@ check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \" completion = ["shtab (>=1.1.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=2.2)"] +enabler = ["pytest-enabler (>=3.4)"] test = ["pyfakefs", "pytest (>=6,!=8.1.*)"] -type = ["pygobject-stubs", "pytest-mypy", "shtab", "types-pywin32"] +type = ["pygobject-stubs", "pytest-mypy (>=1.0.1)", "shtab", "types-pywin32"] [[package]] name = "markdown-it-py" @@ -866,6 +1069,7 @@ description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.8" groups = ["dev"] +markers = "python_full_version < \"3.14.0\" or platform_python_implementation == \"PyPy\"" files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, @@ -884,6 +1088,31 @@ profiling = ["gprof2dot"] rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] +[[package]] +name = "markdown-it-py" +version = "4.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +markers = "python_version >= \"3.14\" and platform_python_implementation != \"PyPy\"" +files = [ + {file = "markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147"}, + {file = "markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "markdown-it-pyrs", "mistletoe (>=1.0,<2.0)", "mistune (>=3.0,<4.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins (>=0.5.0)"] +profiling = ["gprof2dot"] +rtd = ["ipykernel", "jupyter_sphinx", "mdit-py-plugins (>=0.5.0)", "myst-parser", "pyyaml", "sphinx", "sphinx-book-theme (>=1.0,<2.0)", "sphinx-copybutton", "sphinx-design"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions", "requests"] + [[package]] name = "mccabe" version = "0.7.0" @@ -910,135 +1139,171 @@ files = [ [[package]] name = "more-itertools" -version = "10.7.0" +version = "10.8.0" description = "More routines for operating on iterables, beyond itertools" optional = false python-versions = ">=3.9" groups = ["dev"] markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" files = [ - {file = "more_itertools-10.7.0-py3-none-any.whl", hash = "sha256:d43980384673cb07d2f7d2d918c616b30c659c089ee23953f601d6609c67510e"}, - {file = "more_itertools-10.7.0.tar.gz", hash = "sha256:9fddd5403be01a94b204faadcff459ec3568cf110265d3c54323e1e866ad29d3"}, + {file = "more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b"}, + {file = "more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd"}, ] [[package]] name = "multidict" -version = "6.5.0" +version = "6.7.0" description = "multidict implementation" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "multidict-6.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2e118a202904623b1d2606d1c8614e14c9444b59d64454b0c355044058066469"}, - {file = "multidict-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a42995bdcaff4e22cb1280ae7752c3ed3fbb398090c6991a2797a4a0e5ed16a9"}, - {file = "multidict-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2261b538145723ca776e55208640fffd7ee78184d223f37c2b40b9edfe0e818a"}, - {file = "multidict-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e5b19f8cd67235fab3e195ca389490415d9fef5a315b1fa6f332925dc924262"}, - {file = "multidict-6.5.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:177b081e4dec67c3320b16b3aa0babc178bbf758553085669382c7ec711e1ec8"}, - {file = "multidict-6.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d30a2cc106a7d116b52ee046207614db42380b62e6b1dd2a50eba47c5ca5eb1"}, - {file = "multidict-6.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a72933bc308d7a64de37f0d51795dbeaceebdfb75454f89035cdfc6a74cfd129"}, - {file = "multidict-6.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96d109e663d032280ef8ef62b50924b2e887d5ddf19e301844a6cb7e91a172a6"}, - {file = "multidict-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b555329c9894332401f03b9a87016f0b707b6fccd4706793ec43b4a639e75869"}, - {file = "multidict-6.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6994bad9d471ef2156f2b6850b51e20ee409c6b9deebc0e57be096be9faffdce"}, - {file = "multidict-6.5.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:b15f817276c96cde9060569023808eec966bd8da56a97e6aa8116f34ddab6534"}, - {file = "multidict-6.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b4bf507c991db535a935b2127cf057a58dbc688c9f309c72080795c63e796f58"}, - {file = "multidict-6.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:60c3f8f13d443426c55f88cf3172547bbc600a86d57fd565458b9259239a6737"}, - {file = "multidict-6.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:a10227168a24420c158747fc201d4279aa9af1671f287371597e2b4f2ff21879"}, - {file = "multidict-6.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e3b1425fe54ccfde66b8cfb25d02be34d5dfd2261a71561ffd887ef4088b4b69"}, - {file = "multidict-6.5.0-cp310-cp310-win32.whl", hash = "sha256:b4e47ef51237841d1087e1e1548071a6ef22e27ed0400c272174fa585277c4b4"}, - {file = "multidict-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:63b3b24fadc7067282c88fae5b2f366d5b3a7c15c021c2838de8c65a50eeefb4"}, - {file = "multidict-6.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:8b2d61afbafc679b7eaf08e9de4fa5d38bd5dc7a9c0a577c9f9588fb49f02dbb"}, - {file = "multidict-6.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8b4bf6bb15a05796a07a248084e3e46e032860c899c7a9b981030e61368dba95"}, - {file = "multidict-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46bb05d50219655c42a4b8fcda9c7ee658a09adbb719c48e65a20284e36328ea"}, - {file = "multidict-6.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:54f524d73f4d54e87e03c98f6af601af4777e4668a52b1bd2ae0a4d6fc7b392b"}, - {file = "multidict-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:529b03600466480ecc502000d62e54f185a884ed4570dee90d9a273ee80e37b5"}, - {file = "multidict-6.5.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:69ad681ad7c93a41ee7005cc83a144b5b34a3838bcf7261e2b5356057b0f78de"}, - {file = "multidict-6.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fe9fada8bc0839466b09fa3f6894f003137942984843ec0c3848846329a36ae"}, - {file = "multidict-6.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f94c6ea6405fcf81baef1e459b209a78cda5442e61b5b7a57ede39d99b5204a0"}, - {file = "multidict-6.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84ca75ad8a39ed75f079a8931435a5b51ee4c45d9b32e1740f99969a5d1cc2ee"}, - {file = "multidict-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be4c08f3a2a6cc42b414496017928d95898964fed84b1b2dace0c9ee763061f9"}, - {file = "multidict-6.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:046a7540cfbb4d5dc846a1fd9843f3ba980c6523f2e0c5b8622b4a5c94138ae6"}, - {file = "multidict-6.5.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:64306121171d988af77d74be0d8c73ee1a69cf6f96aea7fa6030c88f32a152dd"}, - {file = "multidict-6.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b4ac1dd5eb0ecf6f7351d5a9137f30a83f7182209c5d37f61614dfdce5714853"}, - {file = "multidict-6.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bab4a8337235365f4111a7011a1f028826ca683834ebd12de4b85e2844359c36"}, - {file = "multidict-6.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a05b5604c5a75df14a63eeeca598d11b2c3745b9008539b70826ea044063a572"}, - {file = "multidict-6.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:67c4a640952371c9ca65b6a710598be246ef3be5ca83ed38c16a7660d3980877"}, - {file = "multidict-6.5.0-cp311-cp311-win32.whl", hash = "sha256:fdeae096ca36c12d8aca2640b8407a9d94e961372c68435bef14e31cce726138"}, - {file = "multidict-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:e2977ef8b7ce27723ee8c610d1bd1765da4f3fbe5a64f9bf1fd3b4770e31fbc0"}, - {file = "multidict-6.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:82d0cf0ea49bae43d9e8c3851e21954eff716259ff42da401b668744d1760bcb"}, - {file = "multidict-6.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1bb986c8ea9d49947bc325c51eced1ada6d8d9b4c5b15fd3fcdc3c93edef5a74"}, - {file = "multidict-6.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:03c0923da300120830fc467e23805d63bbb4e98b94032bd863bc7797ea5fa653"}, - {file = "multidict-6.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4c78d5ec00fdd35c91680ab5cf58368faad4bd1a8721f87127326270248de9bc"}, - {file = "multidict-6.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadc3cb78be90a887f8f6b73945b840da44b4a483d1c9750459ae69687940c97"}, - {file = "multidict-6.5.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5b02e1ca495d71e07e652e4cef91adae3bf7ae4493507a263f56e617de65dafc"}, - {file = "multidict-6.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7fe92a62326eef351668eec4e2dfc494927764a0840a1895cff16707fceffcd3"}, - {file = "multidict-6.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7673ee4f63879ecd526488deb1989041abcb101b2d30a9165e1e90c489f3f7fb"}, - {file = "multidict-6.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa097ae2a29f573de7e2d86620cbdda5676d27772d4ed2669cfa9961a0d73955"}, - {file = "multidict-6.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:300da0fa4f8457d9c4bd579695496116563409e676ac79b5e4dca18e49d1c308"}, - {file = "multidict-6.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9a19bd108c35877b57393243d392d024cfbfdefe759fd137abb98f6fc910b64c"}, - {file = "multidict-6.5.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f32a1777465a35c35ddbbd7fc1293077938a69402fcc59e40b2846d04a120dd"}, - {file = "multidict-6.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9cc1e10c14ce8112d1e6d8971fe3cdbe13e314f68bea0e727429249d4a6ce164"}, - {file = "multidict-6.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e95c5e07a06594bdc288117ca90e89156aee8cb2d7c330b920d9c3dd19c05414"}, - {file = "multidict-6.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:40ff26f58323795f5cd2855e2718a1720a1123fb90df4553426f0efd76135462"}, - {file = "multidict-6.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:76803a29fd71869a8b59c2118c9dcfb3b8f9c8723e2cce6baeb20705459505cf"}, - {file = "multidict-6.5.0-cp312-cp312-win32.whl", hash = "sha256:df7ecbc65a53a2ce1b3a0c82e6ad1a43dcfe7c6137733f9176a92516b9f5b851"}, - {file = "multidict-6.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ec1c3fbbb0b655a6540bce408f48b9a7474fd94ed657dcd2e890671fefa7743"}, - {file = "multidict-6.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:2d24a00d34808b22c1f15902899b9d82d0faeca9f56281641c791d8605eacd35"}, - {file = "multidict-6.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:53d92df1752df67a928fa7f884aa51edae6f1cf00eeb38cbcf318cf841c17456"}, - {file = "multidict-6.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:680210de2c38eef17ce46b8df8bf2c1ece489261a14a6e43c997d49843a27c99"}, - {file = "multidict-6.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e279259bcb936732bfa1a8eec82b5d2352b3df69d2fa90d25808cfc403cee90a"}, - {file = "multidict-6.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1c185fc1069781e3fc8b622c4331fb3b433979850392daa5efbb97f7f9959bb"}, - {file = "multidict-6.5.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6bb5f65ff91daf19ce97f48f63585e51595539a8a523258b34f7cef2ec7e0617"}, - {file = "multidict-6.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8646b4259450c59b9286db280dd57745897897284f6308edbdf437166d93855"}, - {file = "multidict-6.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d245973d4ecc04eea0a8e5ebec7882cf515480036e1b48e65dffcfbdf86d00be"}, - {file = "multidict-6.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a133e7ddc9bc7fb053733d0ff697ce78c7bf39b5aec4ac12857b6116324c8d75"}, - {file = "multidict-6.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80d696fa38d738fcebfd53eec4d2e3aeb86a67679fd5e53c325756682f152826"}, - {file = "multidict-6.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:20d30c9410ac3908abbaa52ee5967a754c62142043cf2ba091e39681bd51d21a"}, - {file = "multidict-6.5.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:6c65068cc026f217e815fa519d8e959a7188e94ec163ffa029c94ca3ef9d4a73"}, - {file = "multidict-6.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e355ac668a8c3e49c2ca8daa4c92f0ad5b705d26da3d5af6f7d971e46c096da7"}, - {file = "multidict-6.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:08db204213d0375a91a381cae0677ab95dd8c67a465eb370549daf6dbbf8ba10"}, - {file = "multidict-6.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ffa58e3e215af8f6536dc837a990e456129857bb6fd546b3991be470abd9597a"}, - {file = "multidict-6.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3e86eb90015c6f21658dbd257bb8e6aa18bdb365b92dd1fba27ec04e58cdc31b"}, - {file = "multidict-6.5.0-cp313-cp313-win32.whl", hash = "sha256:f34a90fbd9959d0f857323bd3c52b3e6011ed48f78d7d7b9e04980b8a41da3af"}, - {file = "multidict-6.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:fcb2aa79ac6aef8d5b709bbfc2fdb1d75210ba43038d70fbb595b35af470ce06"}, - {file = "multidict-6.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:6dcee5e7e92060b4bb9bb6f01efcbb78c13d0e17d9bc6eec71660dd71dc7b0c2"}, - {file = "multidict-6.5.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:cbbc88abea2388fde41dd574159dec2cda005cb61aa84950828610cb5010f21a"}, - {file = "multidict-6.5.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:70b599f70ae6536e5976364d3c3cf36f40334708bd6cebdd1e2438395d5e7676"}, - {file = "multidict-6.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:828bab777aa8d29d59700018178061854e3a47727e0611cb9bec579d3882de3b"}, - {file = "multidict-6.5.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9695fc1462f17b131c111cf0856a22ff154b0480f86f539d24b2778571ff94d"}, - {file = "multidict-6.5.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b5ac6ebaf5d9814b15f399337ebc6d3a7f4ce9331edd404e76c49a01620b68d"}, - {file = "multidict-6.5.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84a51e3baa77ded07be4766a9e41d977987b97e49884d4c94f6d30ab6acaee14"}, - {file = "multidict-6.5.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8de67f79314d24179e9b1869ed15e88d6ba5452a73fc9891ac142e0ee018b5d6"}, - {file = "multidict-6.5.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17f78a52c214481d30550ec18208e287dfc4736f0c0148208334b105fd9e0887"}, - {file = "multidict-6.5.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2966d0099cb2e2039f9b0e73e7fd5eb9c85805681aa2a7f867f9d95b35356921"}, - {file = "multidict-6.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:86fb42ed5ed1971c642cc52acc82491af97567534a8e381a8d50c02169c4e684"}, - {file = "multidict-6.5.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:4e990cbcb6382f9eae4ec720bcac6a1351509e6fc4a5bb70e4984b27973934e6"}, - {file = "multidict-6.5.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d99a59d64bb1f7f2117bec837d9e534c5aeb5dcedf4c2b16b9753ed28fdc20a3"}, - {file = "multidict-6.5.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:e8ef15cc97c9890212e1caf90f0d63f6560e1e101cf83aeaf63a57556689fb34"}, - {file = "multidict-6.5.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:b8a09aec921b34bd8b9f842f0bcfd76c6a8c033dc5773511e15f2d517e7e1068"}, - {file = "multidict-6.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ff07b504c23b67f2044533244c230808a1258b3493aaf3ea2a0785f70b7be461"}, - {file = "multidict-6.5.0-cp313-cp313t-win32.whl", hash = "sha256:9232a117341e7e979d210e41c04e18f1dc3a1d251268df6c818f5334301274e1"}, - {file = "multidict-6.5.0-cp313-cp313t-win_amd64.whl", hash = "sha256:44cb5c53fb2d4cbcee70a768d796052b75d89b827643788a75ea68189f0980a1"}, - {file = "multidict-6.5.0-cp313-cp313t-win_arm64.whl", hash = "sha256:51d33fafa82640c0217391d4ce895d32b7e84a832b8aee0dcc1b04d8981ec7f4"}, - {file = "multidict-6.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c0078358470da8dc90c37456f4a9cde9f86200949a048d53682b9cd21e5bbf2b"}, - {file = "multidict-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5cc7968b7d1bf8b973c307d38aa3a2f2c783f149bcac855944804252f1df5105"}, - {file = "multidict-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ad73a60e11aa92f1f2c9330efdeaac4531b719fc568eb8d312fd4112f34cc18"}, - {file = "multidict-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3233f21abdcd180b2624eb6988a1e1287210e99bca986d8320afca5005d85844"}, - {file = "multidict-6.5.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:bee5c0b79fca78fd2ab644ca4dc831ecf793eb6830b9f542ee5ed2c91bc35a0e"}, - {file = "multidict-6.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e053a4d690f4352ce46583080fefade9a903ce0fa9d820db1be80bdb9304fa2f"}, - {file = "multidict-6.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42bdee30424c1f4dcda96e07ac60e2a4ede8a89f8ae2f48b5e4ccc060f294c52"}, - {file = "multidict-6.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58b2ded1a7982cf7b8322b0645713a0086b2b3cf5bb9f7c01edfc1a9f98d20dc"}, - {file = "multidict-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f805b8b951d1fadc5bc18c3c93e509608ac5a883045ee33bc22e28806847c20"}, - {file = "multidict-6.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2540395b63723da748f850568357a39cd8d8d4403ca9439f9fcdad6dd423c780"}, - {file = "multidict-6.5.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:c96aedff25f4e47b6697ba048b2c278f7caa6df82c7c3f02e077bcc8d47b4b76"}, - {file = "multidict-6.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e80de5ad995de210fd02a65c2350649b8321d09bd2e44717eaefb0f5814503e8"}, - {file = "multidict-6.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6cb9bcedd9391b313e5ec2fb3aa07c03e050550e7b9e4646c076d5c24ba01532"}, - {file = "multidict-6.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a7d130ed7a112e25ab47309962ecafae07d073316f9d158bc7b3936b52b80121"}, - {file = "multidict-6.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:95750a9a9741cd1855d1b6cb4c6031ae01c01ad38d280217b64bfae986d39d56"}, - {file = "multidict-6.5.0-cp39-cp39-win32.whl", hash = "sha256:7f78caf409914f108f4212b53a9033abfdc2cbab0647e9ac3a25bb0f21ab43d2"}, - {file = "multidict-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:220c74009507e847a3a6fc5375875f2a2e05bd9ce28cf607be0e8c94600f4472"}, - {file = "multidict-6.5.0-cp39-cp39-win_arm64.whl", hash = "sha256:d98f4ac9c1ede7e9d04076e2e6d967e15df0079a6381b297270f6bcab661195e"}, - {file = "multidict-6.5.0-py3-none-any.whl", hash = "sha256:5634b35f225977605385f56153bd95a7133faffc0ffe12ad26e10517537e8dfc"}, - {file = "multidict-6.5.0.tar.gz", hash = "sha256:942bd8002492ba819426a8d7aefde3189c1b87099cdf18aaaefefcf7f3f7b6d2"}, + {file = "multidict-6.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9f474ad5acda359c8758c8accc22032c6abe6dc87a8be2440d097785e27a9349"}, + {file = "multidict-6.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a9db5a870f780220e931d0002bbfd88fb53aceb6293251e2c839415c1b20e"}, + {file = "multidict-6.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03ca744319864e92721195fa28c7a3b2bc7b686246b35e4078c1e4d0eb5466d3"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f0e77e3c0008bc9316e662624535b88d360c3a5d3f81e15cf12c139a75250046"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08325c9e5367aa379a3496aa9a022fe8837ff22e00b94db256d3a1378c76ab32"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e2862408c99f84aa571ab462d25236ef9cb12a602ea959ba9c9009a54902fc73"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d72a9a2d885f5c208b0cb91ff2ed43636bb7e345ec839ff64708e04f69a13cc"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:478cc36476687bac1514d651cbbaa94b86b0732fb6855c60c673794c7dd2da62"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6843b28b0364dc605f21481c90fadb5f60d9123b442eb8a726bb74feef588a84"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:23bfeee5316266e5ee2d625df2d2c602b829435fc3a235c2ba2131495706e4a0"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:680878b9f3d45c31e1f730eef731f9b0bc1da456155688c6745ee84eb818e90e"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:eb866162ef2f45063acc7a53a88ef6fe8bf121d45c30ea3c9cd87ce7e191a8d4"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:df0e3bf7993bdbeca5ac25aa859cf40d39019e015c9c91809ba7093967f7a648"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:661709cdcd919a2ece2234f9bae7174e5220c80b034585d7d8a755632d3e2111"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:096f52730c3fb8ed419db2d44391932b63891b2c5ed14850a7e215c0ba9ade36"}, + {file = "multidict-6.7.0-cp310-cp310-win32.whl", hash = "sha256:afa8a2978ec65d2336305550535c9c4ff50ee527914328c8677b3973ade52b85"}, + {file = "multidict-6.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:b15b3afff74f707b9275d5ba6a91ae8f6429c3ffb29bbfd216b0b375a56f13d7"}, + {file = "multidict-6.7.0-cp310-cp310-win_arm64.whl", hash = "sha256:4b73189894398d59131a66ff157837b1fafea9974be486d036bb3d32331fdbf0"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4d409aa42a94c0b3fa617708ef5276dfe81012ba6753a0370fcc9d0195d0a1fc"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14c9e076eede3b54c636f8ce1c9c252b5f057c62131211f0ceeec273810c9721"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c09703000a9d0fa3c3404b27041e574cc7f4df4c6563873246d0e11812a94b6"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a265acbb7bb33a3a2d626afbe756371dce0279e7b17f4f4eda406459c2b5ff1c"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51cb455de290ae462593e5b1cb1118c5c22ea7f0d3620d9940bf695cea5a4bd7"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:db99677b4457c7a5c5a949353e125ba72d62b35f74e26da141530fbb012218a7"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f470f68adc395e0183b92a2f4689264d1ea4b40504a24d9882c27375e6662bb9"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0db4956f82723cc1c270de9c6e799b4c341d327762ec78ef82bb962f79cc07d8"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e56d780c238f9e1ae66a22d2adf8d16f485381878250db8d496623cd38b22bd"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d14baca2ee12c1a64740d4531356ba50b82543017f3ad6de0deb943c5979abb"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:295a92a76188917c7f99cda95858c822f9e4aae5824246bba9b6b44004ddd0a6"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39f1719f57adbb767ef592a50ae5ebb794220d1188f9ca93de471336401c34d2"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0a13fb8e748dfc94749f622de065dd5c1def7e0d2216dba72b1d8069a389c6ff"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e3aa16de190d29a0ea1b48253c57d99a68492c8dd8948638073ab9e74dc9410b"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a048ce45dcdaaf1defb76b2e684f997fb5abf74437b6cb7b22ddad934a964e34"}, + {file = "multidict-6.7.0-cp311-cp311-win32.whl", hash = "sha256:a90af66facec4cebe4181b9e62a68be65e45ac9b52b67de9eec118701856e7ff"}, + {file = "multidict-6.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:95b5ffa4349df2887518bb839409bcf22caa72d82beec453216802f475b23c81"}, + {file = "multidict-6.7.0-cp311-cp311-win_arm64.whl", hash = "sha256:329aa225b085b6f004a4955271a7ba9f1087e39dcb7e65f6284a988264a63912"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8a3862568a36d26e650a19bb5cbbba14b71789032aebc0423f8cc5f150730184"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:960c60b5849b9b4f9dcc9bea6e3626143c252c74113df2c1540aebce70209b45"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2049be98fb57a31b4ccf870bf377af2504d4ae35646a19037ec271e4c07998aa"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0934f3843a1860dd465d38895c17fce1f1cb37295149ab05cd1b9a03afacb2a7"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3e34f3a1b8131ba06f1a73adab24f30934d148afcd5f5de9a73565a4404384e"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:efbb54e98446892590dc2458c19c10344ee9a883a79b5cec4bc34d6656e8d546"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a35c5fc61d4f51eb045061e7967cfe3123d622cd500e8868e7c0c592a09fedc4"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29fe6740ebccba4175af1b9b87bf553e9c15cd5868ee967e010efcf94e4fd0f1"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:123e2a72e20537add2f33a79e605f6191fba2afda4cbb876e35c1a7074298a7d"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b284e319754366c1aee2267a2036248b24eeb17ecd5dc16022095e747f2f4304"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:803d685de7be4303b5a657b76e2f6d1240e7e0a8aa2968ad5811fa2285553a12"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c04a328260dfd5db8c39538f999f02779012268f54614902d0afc775d44e0a62"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8a19cdb57cd3df4cd865849d93ee14920fb97224300c88501f16ecfa2604b4e0"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b2fd74c52accced7e75de26023b7dccee62511a600e62311b918ec5c168fc2a"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3e8bfdd0e487acf992407a140d2589fe598238eaeffa3da8448d63a63cd363f8"}, + {file = "multidict-6.7.0-cp312-cp312-win32.whl", hash = "sha256:dd32a49400a2c3d52088e120ee00c1e3576cbff7e10b98467962c74fdb762ed4"}, + {file = "multidict-6.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:92abb658ef2d7ef22ac9f8bb88e8b6c3e571671534e029359b6d9e845923eb1b"}, + {file = "multidict-6.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:490dab541a6a642ce1a9d61a4781656b346a55c13038f0b1244653828e3a83ec"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bee7c0588aa0076ce77c0ea5d19a68d76ad81fcd9fe8501003b9a24f9d4000f6"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7ef6b61cad77091056ce0e7ce69814ef72afacb150b7ac6a3e9470def2198159"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c0359b1ec12b1d6849c59f9d319610b7f20ef990a6d454ab151aa0e3b9f78ca"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cd240939f71c64bd658f186330603aac1a9a81bf6273f523fca63673cb7378a8"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60a4d75718a5efa473ebd5ab685786ba0c67b8381f781d1be14da49f1a2dc60"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53a42d364f323275126aff81fb67c5ca1b7a04fda0546245730a55c8c5f24bc4"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3b29b980d0ddbecb736735ee5bef69bb2ddca56eff603c86f3f29a1128299b4f"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f8a93b1c0ed2d04b97a5e9336fd2d33371b9a6e29ab7dd6503d63407c20ffbaf"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ff96e8815eecacc6645da76c413eb3b3d34cfca256c70b16b286a687d013c32"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7516c579652f6a6be0e266aec0acd0db80829ca305c3d771ed898538804c2036"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:040f393368e63fb0f3330e70c26bfd336656bed925e5cbe17c9da839a6ab13ec"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b3bc26a951007b1057a1c543af845f1c7e3e71cc240ed1ace7bf4484aa99196e"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7b022717c748dd1992a83e219587aabe45980d88969f01b316e78683e6285f64"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:9600082733859f00d79dee64effc7aef1beb26adb297416a4ad2116fd61374bd"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:94218fcec4d72bc61df51c198d098ce2b378e0ccbac41ddbed5ef44092913288"}, + {file = "multidict-6.7.0-cp313-cp313-win32.whl", hash = "sha256:a37bd74c3fa9d00be2d7b8eca074dc56bd8077ddd2917a839bd989612671ed17"}, + {file = "multidict-6.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:30d193c6cc6d559db42b6bcec8a5d395d34d60c9877a0b71ecd7c204fcf15390"}, + {file = "multidict-6.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:ea3334cabe4d41b7ccd01e4d349828678794edbc2d3ae97fc162a3312095092e"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ad9ce259f50abd98a1ca0aa6e490b58c316a0fce0617f609723e40804add2c00"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07f5594ac6d084cbb5de2df218d78baf55ef150b91f0ff8a21cc7a2e3a5a58eb"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0591b48acf279821a579282444814a2d8d0af624ae0bc600aa4d1b920b6e924b"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:749a72584761531d2b9467cfbdfd29487ee21124c304c4b6cb760d8777b27f9c"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b4c3d199f953acd5b446bf7c0de1fe25d94e09e79086f8dc2f48a11a129cdf1"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9fb0211dfc3b51efea2f349ec92c114d7754dd62c01f81c3e32b765b70c45c9b"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a027ec240fe73a8d6281872690b988eed307cd7d91b23998ff35ff577ca688b5"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1d964afecdf3a8288789df2f5751dc0a8261138c3768d9af117ed384e538fad"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caf53b15b1b7df9fbd0709aa01409000a2b4dd03a5f6f5cc548183c7c8f8b63c"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:654030da3197d927f05a536a66186070e98765aa5142794c9904555d3a9d8fb5"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2090d3718829d1e484706a2f525e50c892237b2bf9b17a79b059cb98cddc2f10"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d2cfeec3f6f45651b3d408c4acec0ebf3daa9bc8a112a084206f5db5d05b754"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:4ef089f985b8c194d341eb2c24ae6e7408c9a0e2e5658699c92f497437d88c3c"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e93a0617cd16998784bf4414c7e40f17a35d2350e5c6f0bd900d3a8e02bd3762"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f0feece2ef8ebc42ed9e2e8c78fc4aa3cf455733b507c09ef7406364c94376c6"}, + {file = "multidict-6.7.0-cp313-cp313t-win32.whl", hash = "sha256:19a1d55338ec1be74ef62440ca9e04a2f001a04d0cc49a4983dc320ff0f3212d"}, + {file = "multidict-6.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3da4fb467498df97e986af166b12d01f05d2e04f978a9c1c680ea1988e0bc4b6"}, + {file = "multidict-6.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:b4121773c49a0776461f4a904cdf6264c88e42218aaa8407e803ca8025872792"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3bab1e4aff7adaa34410f93b1f8e57c4b36b9af0426a76003f441ee1d3c7e842"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b8512bac933afc3e45fb2b18da8e59b78d4f408399a960339598374d4ae3b56b"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:79dcf9e477bc65414ebfea98ffd013cb39552b5ecd62908752e0e413d6d06e38"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:31bae522710064b5cbeddaf2e9f32b1abab70ac6ac91d42572502299e9953128"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a0df7ff02397bb63e2fd22af2c87dfa39e8c7f12947bc524dbdc528282c7e34"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7a0222514e8e4c514660e182d5156a415c13ef0aabbd71682fc714e327b95e99"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2397ab4daaf2698eb51a76721e98db21ce4f52339e535725de03ea962b5a3202"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8891681594162635948a636c9fe0ff21746aeb3dd5463f6e25d9bea3a8a39ca1"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18706cc31dbf402a7945916dd5cddf160251b6dab8a2c5f3d6d5a55949f676b3"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f844a1bbf1d207dd311a56f383f7eda2d0e134921d45751842d8235e7778965d"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4393e3581e84e5645506923816b9cc81f5609a778c7e7534054091acc64d1c6"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:fbd18dc82d7bf274b37aa48d664534330af744e03bccf696d6f4c6042e7d19e7"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b6234e14f9314731ec45c42fc4554b88133ad53a09092cc48a88e771c125dadb"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:08d4379f9744d8f78d98c8673c06e202ffa88296f009c71bbafe8a6bf847d01f"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9fe04da3f79387f450fd0061d4dd2e45a72749d31bf634aecc9e27f24fdc4b3f"}, + {file = "multidict-6.7.0-cp314-cp314-win32.whl", hash = "sha256:fbafe31d191dfa7c4c51f7a6149c9fb7e914dcf9ffead27dcfd9f1ae382b3885"}, + {file = "multidict-6.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2f67396ec0310764b9222a1728ced1ab638f61aadc6226f17a71dd9324f9a99c"}, + {file = "multidict-6.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:ba672b26069957ee369cfa7fc180dde1fc6f176eaf1e6beaf61fbebbd3d9c000"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:c1dcc7524066fa918c6a27d61444d4ee7900ec635779058571f70d042d86ed63"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:27e0b36c2d388dc7b6ced3406671b401e84ad7eb0656b8f3a2f46ed0ce483718"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a7baa46a22e77f0988e3b23d4ede5513ebec1929e34ee9495be535662c0dfe2"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7bf77f54997a9166a2f5675d1201520586439424c2511723a7312bdb4bcc034e"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e011555abada53f1578d63389610ac8a5400fc70ce71156b0aa30d326f1a5064"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:28b37063541b897fd6a318007373930a75ca6d6ac7c940dbe14731ffdd8d498e"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05047ada7a2fde2631a0ed706f1fd68b169a681dfe5e4cf0f8e4cb6618bbc2cd"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:716133f7d1d946a4e1b91b1756b23c088881e70ff180c24e864c26192ad7534a"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d1bed1b467ef657f2a0ae62844a607909ef1c6889562de5e1d505f74457d0b96"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ca43bdfa5d37bd6aee89d85e1d0831fb86e25541be7e9d376ead1b28974f8e5e"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:44b546bd3eb645fd26fb949e43c02a25a2e632e2ca21a35e2e132c8105dc8599"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a6ef16328011d3f468e7ebc326f24c1445f001ca1dec335b2f8e66bed3006394"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:5aa873cbc8e593d361ae65c68f85faadd755c3295ea2c12040ee146802f23b38"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:3d7b6ccce016e29df4b7ca819659f516f0bc7a4b3efa3bb2012ba06431b044f9"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:171b73bd4ee683d307599b66793ac80981b06f069b62eea1c9e29c9241aa66b0"}, + {file = "multidict-6.7.0-cp314-cp314t-win32.whl", hash = "sha256:b2d7f80c4e1fd010b07cb26820aae86b7e73b681ee4889684fb8d2d4537aab13"}, + {file = "multidict-6.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:09929cab6fcb68122776d575e03c6cc64ee0b8fca48d17e135474b042ce515cd"}, + {file = "multidict-6.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:cc41db090ed742f32bd2d2c721861725e6109681eddf835d0a82bd3a5c382827"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:363eb68a0a59bd2303216d2346e6c441ba10d36d1f9969fcb6f1ba700de7bb5c"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d874eb056410ca05fed180b6642e680373688efafc7f077b2a2f61811e873a40"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8b55d5497b51afdfde55925e04a022f1de14d4f4f25cdfd4f5d9b0aa96166851"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f8e5c0031b90ca9ce555e2e8fd5c3b02a25f14989cbc310701823832c99eb687"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cf41880c991716f3c7cec48e2f19ae4045fc9db5fc9cff27347ada24d710bb5"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8cfc12a8630a29d601f48d47787bd7eb730e475e83edb5d6c5084317463373eb"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3996b50c3237c4aec17459217c1e7bbdead9a22a0fcd3c365564fbd16439dde6"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7f5170993a0dd3ab871c74f45c0a21a4e2c37a2f2b01b5f722a2ad9c6650469e"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ec81878ddf0e98817def1e77d4f50dae5ef5b0e4fe796fae3bd674304172416e"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9281bf5b34f59afbc6b1e477a372e9526b66ca446f4bf62592839c195a718b32"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:68af405971779d8b37198726f2b6fe3955db846fee42db7a4286fc542203934c"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3ba3ef510467abb0667421a286dc906e30eb08569365f5cdb131d7aff7c2dd84"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b61189b29081a20c7e4e0b49b44d5d44bb0dc92be3c6d06a11cc043f81bf9329"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fb287618b9c7aa3bf8d825f02d9201b2f13078a5ed3b293c8f4d953917d84d5e"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:521f33e377ff64b96c4c556b81c55d0cfffb96a11c194fd0c3f1e56f3d8dd5a4"}, + {file = "multidict-6.7.0-cp39-cp39-win32.whl", hash = "sha256:ce8fdc2dca699f8dbf055a61d73eaa10482569ad20ee3c36ef9641f69afa8c91"}, + {file = "multidict-6.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:7e73299c99939f089dd9b2120a04a516b95cdf8c1cd2b18c53ebf0de80b1f18f"}, + {file = "multidict-6.7.0-cp39-cp39-win_arm64.whl", hash = "sha256:6bdce131e14b04fd34a809b6380dbfd826065c3e2fe8a50dbae659fa0c390546"}, + {file = "multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3"}, + {file = "multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5"}, ] [package.dependencies] @@ -1058,36 +1323,38 @@ files = [ [[package]] name = "nh3" -version = "0.2.21" +version = "0.3.2" description = "Python binding to Ammonia HTML sanitizer Rust crate" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "nh3-0.2.21-cp313-cp313t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:fcff321bd60c6c5c9cb4ddf2554e22772bb41ebd93ad88171bbbb6f271255286"}, - {file = "nh3-0.2.21-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31eedcd7d08b0eae28ba47f43fd33a653b4cdb271d64f1aeda47001618348fde"}, - {file = "nh3-0.2.21-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d426d7be1a2f3d896950fe263332ed1662f6c78525b4520c8e9861f8d7f0d243"}, - {file = "nh3-0.2.21-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9d67709bc0d7d1f5797b21db26e7a8b3d15d21c9c5f58ccfe48b5328483b685b"}, - {file = "nh3-0.2.21-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:55823c5ea1f6b267a4fad5de39bc0524d49a47783e1fe094bcf9c537a37df251"}, - {file = "nh3-0.2.21-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:818f2b6df3763e058efa9e69677b5a92f9bc0acff3295af5ed013da544250d5b"}, - {file = "nh3-0.2.21-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b3b5c58161e08549904ac4abd450dacd94ff648916f7c376ae4b2c0652b98ff9"}, - {file = "nh3-0.2.21-cp313-cp313t-win32.whl", hash = "sha256:637d4a10c834e1b7d9548592c7aad760611415fcd5bd346f77fd8a064309ae6d"}, - {file = "nh3-0.2.21-cp313-cp313t-win_amd64.whl", hash = "sha256:713d16686596e556b65e7f8c58328c2df63f1a7abe1277d87625dcbbc012ef82"}, - {file = "nh3-0.2.21-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:a772dec5b7b7325780922dd904709f0f5f3a79fbf756de5291c01370f6df0967"}, - {file = "nh3-0.2.21-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d002b648592bf3033adfd875a48f09b8ecc000abd7f6a8769ed86b6ccc70c759"}, - {file = "nh3-0.2.21-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2a5174551f95f2836f2ad6a8074560f261cf9740a48437d6151fd2d4d7d617ab"}, - {file = "nh3-0.2.21-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b8d55ea1fc7ae3633d758a92aafa3505cd3cc5a6e40470c9164d54dff6f96d42"}, - {file = "nh3-0.2.21-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6ae319f17cd8960d0612f0f0ddff5a90700fa71926ca800e9028e7851ce44a6f"}, - {file = "nh3-0.2.21-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63ca02ac6f27fc80f9894409eb61de2cb20ef0a23740c7e29f9ec827139fa578"}, - {file = "nh3-0.2.21-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5f77e62aed5c4acad635239ac1290404c7e940c81abe561fd2af011ff59f585"}, - {file = "nh3-0.2.21-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:087ffadfdcd497658c3adc797258ce0f06be8a537786a7217649fc1c0c60c293"}, - {file = "nh3-0.2.21-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ac7006c3abd097790e611fe4646ecb19a8d7f2184b882f6093293b8d9b887431"}, - {file = "nh3-0.2.21-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:6141caabe00bbddc869665b35fc56a478eb774a8c1dfd6fba9fe1dfdf29e6efa"}, - {file = "nh3-0.2.21-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:20979783526641c81d2f5bfa6ca5ccca3d1e4472474b162c6256745fbfe31cd1"}, - {file = "nh3-0.2.21-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a7ea28cd49293749d67e4fcf326c554c83ec912cd09cd94aa7ec3ab1921c8283"}, - {file = "nh3-0.2.21-cp38-abi3-win32.whl", hash = "sha256:6c9c30b8b0d291a7c5ab0967ab200598ba33208f754f2f4920e9343bdd88f79a"}, - {file = "nh3-0.2.21-cp38-abi3-win_amd64.whl", hash = "sha256:bb0014948f04d7976aabae43fcd4cb7f551f9f8ce785a4c9ef66e6c2590f8629"}, - {file = "nh3-0.2.21.tar.gz", hash = "sha256:4990e7ee6a55490dbf00d61a6f476c9a3258e31e711e13713b2ea7d6616f670e"}, + {file = "nh3-0.3.2-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:d18957a90806d943d141cc5e4a0fefa1d77cf0d7a156878bf9a66eed52c9cc7d"}, + {file = "nh3-0.3.2-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45c953e57028c31d473d6b648552d9cab1efe20a42ad139d78e11d8f42a36130"}, + {file = "nh3-0.3.2-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2c9850041b77a9147d6bbd6dbbf13eeec7009eb60b44e83f07fcb2910075bf9b"}, + {file = "nh3-0.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:403c11563e50b915d0efdb622866d1d9e4506bce590ef7da57789bf71dd148b5"}, + {file = "nh3-0.3.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:0dca4365db62b2d71ff1620ee4f800c4729849906c5dd504ee1a7b2389558e31"}, + {file = "nh3-0.3.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0fe7ee035dd7b2290715baf29cb27167dddd2ff70ea7d052c958dbd80d323c99"}, + {file = "nh3-0.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a40202fd58e49129764f025bbaae77028e420f1d5b3c8e6f6fd3a6490d513868"}, + {file = "nh3-0.3.2-cp314-cp314t-win32.whl", hash = "sha256:1f9ba555a797dbdcd844b89523f29cdc90973d8bd2e836ea6b962cf567cadd93"}, + {file = "nh3-0.3.2-cp314-cp314t-win_amd64.whl", hash = "sha256:dce4248edc427c9b79261f3e6e2b3ecbdd9b88c267012168b4a7b3fc6fd41d13"}, + {file = "nh3-0.3.2-cp314-cp314t-win_arm64.whl", hash = "sha256:019ecbd007536b67fdf76fab411b648fb64e2257ca3262ec80c3425c24028c80"}, + {file = "nh3-0.3.2-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:7064ccf5ace75825bd7bf57859daaaf16ed28660c1c6b306b649a9eda4b54b1e"}, + {file = "nh3-0.3.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8745454cdd28bbbc90861b80a0111a195b0e3961b9fa2e672be89eb199fa5d8"}, + {file = "nh3-0.3.2-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72d67c25a84579f4a432c065e8b4274e53b7cf1df8f792cf846abfe2c3090866"}, + {file = "nh3-0.3.2-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:13398e676a14d6233f372c75f52d5ae74f98210172991f7a3142a736bd92b131"}, + {file = "nh3-0.3.2-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03d617e5c8aa7331bd2659c654e021caf9bba704b109e7b2b28b039a00949fe5"}, + {file = "nh3-0.3.2-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2f55c4d2d5a207e74eefe4d828067bbb01300e06e2a7436142f915c5928de07"}, + {file = "nh3-0.3.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bb18403f02b655a1bbe4e3a4696c2ae1d6ae8f5991f7cacb684b1ae27e6c9f7"}, + {file = "nh3-0.3.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6d66f41672eb4060cf87c037f760bdbc6847852ca9ef8e9c5a5da18f090abf87"}, + {file = "nh3-0.3.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f97f8b25cb2681d25e2338148159447e4d689aafdccfcf19e61ff7db3905768a"}, + {file = "nh3-0.3.2-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:2ab70e8c6c7d2ce953d2a58102eefa90c2d0a5ed7aa40c7e29a487bc5e613131"}, + {file = "nh3-0.3.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:1710f3901cd6440ca92494ba2eb6dc260f829fa8d9196b659fa10de825610ce0"}, + {file = "nh3-0.3.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:91e9b001101fb4500a2aafe3e7c92928d85242d38bf5ac0aba0b7480da0a4cd6"}, + {file = "nh3-0.3.2-cp38-abi3-win32.whl", hash = "sha256:169db03df90da63286e0560ea0efa9b6f3b59844a9735514a1d47e6bb2c8c61b"}, + {file = "nh3-0.3.2-cp38-abi3-win_amd64.whl", hash = "sha256:562da3dca7a17f9077593214a9781a94b8d76de4f158f8c895e62f09573945fe"}, + {file = "nh3-0.3.2-cp38-abi3-win_arm64.whl", hash = "sha256:cf5964d54edd405e68583114a7cba929468bcd7db5e676ae38ee954de1cfc104"}, + {file = "nh3-0.3.2.tar.gz", hash = "sha256:f394759a06df8b685a4ebfb1874fb67a9cbfd58c64fc5ed587a663c0e63ec376"}, ] [[package]] @@ -1131,14 +1398,15 @@ files = [ [[package]] name = "platformdirs" -version = "4.3.8" +version = "4.4.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.9" groups = ["dev"] +markers = "python_full_version < \"3.14.0\" or platform_python_implementation == \"PyPy\"" files = [ - {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, - {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, + {file = "platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85"}, + {file = "platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf"}, ] [package.extras] @@ -1146,6 +1414,24 @@ docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-a test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] type = ["mypy (>=1.14.1)"] +[[package]] +name = "platformdirs" +version = "4.5.1" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.10" +groups = ["dev"] +markers = "python_version >= \"3.14\" and platform_python_implementation != \"PyPy\"" +files = [ + {file = "platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31"}, + {file = "platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda"}, +] + +[package.extras] +docs = ["furo (>=2025.9.25)", "proselint (>=0.14)", "sphinx (>=8.2.3)", "sphinx-autodoc-typehints (>=3.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.4.2)", "pytest-cov (>=7)", "pytest-mock (>=3.15.1)"] +type = ["mypy (>=1.18.2)"] + [[package]] name = "pluggy" version = "1.6.0" @@ -1176,110 +1462,134 @@ files = [ [[package]] name = "propcache" -version = "0.3.2" +version = "0.4.1" description = "Accelerated property cache" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "propcache-0.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:22d9962a358aedbb7a2e36187ff273adeaab9743373a272976d2e348d08c7770"}, - {file = "propcache-0.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d0fda578d1dc3f77b6b5a5dce3b9ad69a8250a891760a548df850a5e8da87f3"}, - {file = "propcache-0.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3def3da3ac3ce41562d85db655d18ebac740cb3fa4367f11a52b3da9d03a5cc3"}, - {file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bec58347a5a6cebf239daba9bda37dffec5b8d2ce004d9fe4edef3d2815137e"}, - {file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55ffda449a507e9fbd4aca1a7d9aa6753b07d6166140e5a18d2ac9bc49eac220"}, - {file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64a67fb39229a8a8491dd42f864e5e263155e729c2e7ff723d6e25f596b1e8cb"}, - {file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da1cf97b92b51253d5b68cf5a2b9e0dafca095e36b7f2da335e27dc6172a614"}, - {file = "propcache-0.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5f559e127134b07425134b4065be45b166183fdcb433cb6c24c8e4149056ad50"}, - {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aff2e4e06435d61f11a428360a932138d0ec288b0a31dd9bd78d200bd4a2b339"}, - {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4927842833830942a5d0a56e6f4839bc484785b8e1ce8d287359794818633ba0"}, - {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6107ddd08b02654a30fb8ad7a132021759d750a82578b94cd55ee2772b6ebea2"}, - {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:70bd8b9cd6b519e12859c99f3fc9a93f375ebd22a50296c3a295028bea73b9e7"}, - {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2183111651d710d3097338dd1893fcf09c9f54e27ff1a8795495a16a469cc90b"}, - {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fb075ad271405dcad8e2a7ffc9a750a3bf70e533bd86e89f0603e607b93aa64c"}, - {file = "propcache-0.3.2-cp310-cp310-win32.whl", hash = "sha256:404d70768080d3d3bdb41d0771037da19d8340d50b08e104ca0e7f9ce55fce70"}, - {file = "propcache-0.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:7435d766f978b4ede777002e6b3b6641dd229cd1da8d3d3106a45770365f9ad9"}, - {file = "propcache-0.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0b8d2f607bd8f80ddc04088bc2a037fdd17884a6fcadc47a96e334d72f3717be"}, - {file = "propcache-0.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06766d8f34733416e2e34f46fea488ad5d60726bb9481d3cddf89a6fa2d9603f"}, - {file = "propcache-0.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2dc1f4a1df4fecf4e6f68013575ff4af84ef6f478fe5344317a65d38a8e6dc9"}, - {file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be29c4f4810c5789cf10ddf6af80b041c724e629fa51e308a7a0fb19ed1ef7bf"}, - {file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59d61f6970ecbd8ff2e9360304d5c8876a6abd4530cb752c06586849ac8a9dc9"}, - {file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62180e0b8dbb6b004baec00a7983e4cc52f5ada9cd11f48c3528d8cfa7b96a66"}, - {file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c144ca294a204c470f18cf4c9d78887810d04a3e2fbb30eea903575a779159df"}, - {file = "propcache-0.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5c2a784234c28854878d68978265617aa6dc0780e53d44b4d67f3651a17a9a2"}, - {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5745bc7acdafa978ca1642891b82c19238eadc78ba2aaa293c6863b304e552d7"}, - {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c0075bf773d66fa8c9d41f66cc132ecc75e5bb9dd7cce3cfd14adc5ca184cb95"}, - {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5f57aa0847730daceff0497f417c9de353c575d8da3579162cc74ac294c5369e"}, - {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:eef914c014bf72d18efb55619447e0aecd5fb7c2e3fa7441e2e5d6099bddff7e"}, - {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2a4092e8549031e82facf3decdbc0883755d5bbcc62d3aea9d9e185549936dcf"}, - {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:85871b050f174bc0bfb437efbdb68aaf860611953ed12418e4361bc9c392749e"}, - {file = "propcache-0.3.2-cp311-cp311-win32.whl", hash = "sha256:36c8d9b673ec57900c3554264e630d45980fd302458e4ac801802a7fd2ef7897"}, - {file = "propcache-0.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53af8cb6a781b02d2ea079b5b853ba9430fcbe18a8e3ce647d5982a3ff69f39"}, - {file = "propcache-0.3.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8de106b6c84506b31c27168582cd3cb3000a6412c16df14a8628e5871ff83c10"}, - {file = "propcache-0.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:28710b0d3975117239c76600ea351934ac7b5ff56e60953474342608dbbb6154"}, - {file = "propcache-0.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce26862344bdf836650ed2487c3d724b00fbfec4233a1013f597b78c1cb73615"}, - {file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca54bd347a253af2cf4544bbec232ab982f4868de0dd684246b67a51bc6b1db"}, - {file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55780d5e9a2ddc59711d727226bb1ba83a22dd32f64ee15594b9392b1f544eb1"}, - {file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:035e631be25d6975ed87ab23153db6a73426a48db688070d925aa27e996fe93c"}, - {file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee6f22b6eaa39297c751d0e80c0d3a454f112f5c6481214fcf4c092074cecd67"}, - {file = "propcache-0.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ca3aee1aa955438c4dba34fc20a9f390e4c79967257d830f137bd5a8a32ed3b"}, - {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7a4f30862869fa2b68380d677cc1c5fcf1e0f2b9ea0cf665812895c75d0ca3b8"}, - {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b77ec3c257d7816d9f3700013639db7491a434644c906a2578a11daf13176251"}, - {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cab90ac9d3f14b2d5050928483d3d3b8fb6b4018893fc75710e6aa361ecb2474"}, - {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0b504d29f3c47cf6b9e936c1852246c83d450e8e063d50562115a6be6d3a2535"}, - {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:ce2ac2675a6aa41ddb2a0c9cbff53780a617ac3d43e620f8fd77ba1c84dcfc06"}, - {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:62b4239611205294cc433845b914131b2a1f03500ff3c1ed093ed216b82621e1"}, - {file = "propcache-0.3.2-cp312-cp312-win32.whl", hash = "sha256:df4a81b9b53449ebc90cc4deefb052c1dd934ba85012aa912c7ea7b7e38b60c1"}, - {file = "propcache-0.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7046e79b989d7fe457bb755844019e10f693752d169076138abf17f31380800c"}, - {file = "propcache-0.3.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ca592ed634a73ca002967458187109265e980422116c0a107cf93d81f95af945"}, - {file = "propcache-0.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9ecb0aad4020e275652ba3975740f241bd12a61f1a784df044cf7477a02bc252"}, - {file = "propcache-0.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f08f1cc28bd2eade7a8a3d2954ccc673bb02062e3e7da09bc75d843386b342f"}, - {file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1a342c834734edb4be5ecb1e9fb48cb64b1e2320fccbd8c54bf8da8f2a84c33"}, - {file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a544caaae1ac73f1fecfae70ded3e93728831affebd017d53449e3ac052ac1e"}, - {file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:310d11aa44635298397db47a3ebce7db99a4cc4b9bbdfcf6c98a60c8d5261cf1"}, - {file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1396592321ac83157ac03a2023aa6cc4a3cc3cfdecb71090054c09e5a7cce3"}, - {file = "propcache-0.3.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cabf5b5902272565e78197edb682017d21cf3b550ba0460ee473753f28d23c1"}, - {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0a2f2235ac46a7aa25bdeb03a9e7060f6ecbd213b1f9101c43b3090ffb971ef6"}, - {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:92b69e12e34869a6970fd2f3da91669899994b47c98f5d430b781c26f1d9f387"}, - {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:54e02207c79968ebbdffc169591009f4474dde3b4679e16634d34c9363ff56b4"}, - {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4adfb44cb588001f68c5466579d3f1157ca07f7504fc91ec87862e2b8e556b88"}, - {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fd3e6019dc1261cd0291ee8919dd91fbab7b169bb76aeef6c716833a3f65d206"}, - {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c181cad81158d71c41a2bce88edce078458e2dd5ffee7eddd6b05da85079f43"}, - {file = "propcache-0.3.2-cp313-cp313-win32.whl", hash = "sha256:8a08154613f2249519e549de2330cf8e2071c2887309a7b07fb56098f5170a02"}, - {file = "propcache-0.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e41671f1594fc4ab0a6dec1351864713cb3a279910ae8b58f884a88a0a632c05"}, - {file = "propcache-0.3.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:9a3cf035bbaf035f109987d9d55dc90e4b0e36e04bbbb95af3055ef17194057b"}, - {file = "propcache-0.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:156c03d07dc1323d8dacaa221fbe028c5c70d16709cdd63502778e6c3ccca1b0"}, - {file = "propcache-0.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74413c0ba02ba86f55cf60d18daab219f7e531620c15f1e23d95563f505efe7e"}, - {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f066b437bb3fa39c58ff97ab2ca351db465157d68ed0440abecb21715eb24b28"}, - {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1304b085c83067914721e7e9d9917d41ad87696bf70f0bc7dee450e9c71ad0a"}, - {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab50cef01b372763a13333b4e54021bdcb291fc9a8e2ccb9c2df98be51bcde6c"}, - {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad3b2a085ec259ad2c2842666b2a0a49dea8463579c606426128925af1ed725"}, - {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:261fa020c1c14deafd54c76b014956e2f86991af198c51139faf41c4d5e83892"}, - {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:46d7f8aa79c927e5f987ee3a80205c987717d3659f035c85cf0c3680526bdb44"}, - {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:6d8f3f0eebf73e3c0ff0e7853f68be638b4043c65a70517bb575eff54edd8dbe"}, - {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:03c89c1b14a5452cf15403e291c0ccd7751d5b9736ecb2c5bab977ad6c5bcd81"}, - {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:0cc17efde71e12bbaad086d679ce575268d70bc123a5a71ea7ad76f70ba30bba"}, - {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:acdf05d00696bc0447e278bb53cb04ca72354e562cf88ea6f9107df8e7fd9770"}, - {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4445542398bd0b5d32df908031cb1b30d43ac848e20470a878b770ec2dcc6330"}, - {file = "propcache-0.3.2-cp313-cp313t-win32.whl", hash = "sha256:f86e5d7cd03afb3a1db8e9f9f6eff15794e79e791350ac48a8c924e6f439f394"}, - {file = "propcache-0.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9704bedf6e7cbe3c65eca4379a9b53ee6a83749f047808cbb5044d40d7d72198"}, - {file = "propcache-0.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a7fad897f14d92086d6b03fdd2eb844777b0c4d7ec5e3bac0fbae2ab0602bbe5"}, - {file = "propcache-0.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1f43837d4ca000243fd7fd6301947d7cb93360d03cd08369969450cc6b2ce3b4"}, - {file = "propcache-0.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:261df2e9474a5949c46e962065d88eb9b96ce0f2bd30e9d3136bcde84befd8f2"}, - {file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e514326b79e51f0a177daab1052bc164d9d9e54133797a3a58d24c9c87a3fe6d"}, - {file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4a996adb6904f85894570301939afeee65f072b4fd265ed7e569e8d9058e4ec"}, - {file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:76cace5d6b2a54e55b137669b30f31aa15977eeed390c7cbfb1dafa8dfe9a701"}, - {file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31248e44b81d59d6addbb182c4720f90b44e1efdc19f58112a3c3a1615fb47ef"}, - {file = "propcache-0.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abb7fa19dbf88d3857363e0493b999b8011eea856b846305d8c0512dfdf8fbb1"}, - {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d81ac3ae39d38588ad0549e321e6f773a4e7cc68e7751524a22885d5bbadf886"}, - {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:cc2782eb0f7a16462285b6f8394bbbd0e1ee5f928034e941ffc444012224171b"}, - {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:db429c19a6c7e8a1c320e6a13c99799450f411b02251fb1b75e6217cf4a14fcb"}, - {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:21d8759141a9e00a681d35a1f160892a36fb6caa715ba0b832f7747da48fb6ea"}, - {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2ca6d378f09adb13837614ad2754fa8afaee330254f404299611bce41a8438cb"}, - {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:34a624af06c048946709f4278b4176470073deda88d91342665d95f7c6270fbe"}, - {file = "propcache-0.3.2-cp39-cp39-win32.whl", hash = "sha256:4ba3fef1c30f306b1c274ce0b8baaa2c3cdd91f645c48f06394068f37d3837a1"}, - {file = "propcache-0.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:7a2368eed65fc69a7a7a40b27f22e85e7627b74216f0846b04ba5c116e191ec9"}, - {file = "propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f"}, - {file = "propcache-0.3.2.tar.gz", hash = "sha256:20d7d62e4e7ef05f221e0db2856b979540686342e7dd9973b815599c7057e168"}, + {file = "propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db"}, + {file = "propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8"}, + {file = "propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c"}, + {file = "propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb"}, + {file = "propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37"}, + {file = "propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f"}, + {file = "propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1"}, + {file = "propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6"}, + {file = "propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75"}, + {file = "propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8"}, + {file = "propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db"}, + {file = "propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66"}, + {file = "propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81"}, + {file = "propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e"}, + {file = "propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1"}, + {file = "propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717"}, + {file = "propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37"}, + {file = "propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144"}, + {file = "propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f"}, + {file = "propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153"}, + {file = "propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455"}, + {file = "propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85"}, + {file = "propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1"}, + {file = "propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3d233076ccf9e450c8b3bc6720af226b898ef5d051a2d145f7d765e6e9f9bcff"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:357f5bb5c377a82e105e44bd3d52ba22b616f7b9773714bff93573988ef0a5fb"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbc3b6dfc728105b2a57c06791eb07a94229202ea75c59db644d7d496b698cac"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:182b51b421f0501952d938dc0b0eb45246a5b5153c50d42b495ad5fb7517c888"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4b536b39c5199b96fc6245eb5fb796c497381d3942f169e44e8e392b29c9ebcc"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:db65d2af507bbfbdcedb254a11149f894169d90488dd3e7190f7cdcb2d6cd57a"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd2dbc472da1f772a4dae4fa24be938a6c544671a912e30529984dd80400cd88"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:daede9cd44e0f8bdd9e6cc9a607fc81feb80fae7a5fc6cecaff0e0bb32e42d00"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:71b749281b816793678ae7f3d0d84bd36e694953822eaad408d682efc5ca18e0"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:0002004213ee1f36cfb3f9a42b5066100c44276b9b72b4e1504cddd3d692e86e"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fe49d0a85038f36ba9e3ffafa1103e61170b28e95b16622e11be0a0ea07c6781"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:99d43339c83aaf4d32bda60928231848eee470c6bda8d02599cc4cebe872d183"}, + {file = "propcache-0.4.1-cp39-cp39-win32.whl", hash = "sha256:a129e76735bc792794d5177069691c3217898b9f5cee2b2661471e52ffe13f19"}, + {file = "propcache-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:948dab269721ae9a87fd16c514a0a2c2a1bdb23a9a61b969b0f9d9ee2968546f"}, + {file = "propcache-0.4.1-cp39-cp39-win_arm64.whl", hash = "sha256:5fd37c406dd6dc85aa743e214cef35dc54bbdd1419baac4f6ae5e5b1a2976938"}, + {file = "propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237"}, + {file = "propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d"}, ] [[package]] @@ -1316,15 +1626,15 @@ files = [ [[package]] name = "pycparser" -version = "2.22" +version = "2.23" description = "C parser in Python" optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\"" +markers = "platform_python_implementation != \"PyPy\" and platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\" and implementation_name != \"PyPy\"" files = [ - {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, - {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, + {file = "pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"}, + {file = "pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"}, ] [[package]] @@ -1341,14 +1651,14 @@ files = [ [[package]] name = "pygments" -version = "2.19.1" +version = "2.19.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, - {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, ] [package.extras] @@ -1356,14 +1666,14 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pytest" -version = "8.4.1" +version = "8.4.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7"}, - {file = "pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"}, + {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"}, + {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"}, ] [package.dependencies] @@ -1408,65 +1718,85 @@ files = [ [[package]] name = "pyyaml" -version = "6.0.2" +version = "6.0.3" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, - {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, - {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, - {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, - {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, - {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, - {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, - {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, - {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, - {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, - {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, - {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, - {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, - {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, - {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, - {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, - {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, + {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"}, + {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"}, + {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"}, + {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"}, + {file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"}, + {file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"}, + {file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"}, + {file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"}, + {file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"}, + {file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"}, + {file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"}, + {file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"}, + {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"}, ] [[package]] @@ -1543,20 +1873,19 @@ idna2008 = ["idna"] [[package]] name = "rich" -version = "14.0.0" +version = "14.2.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" groups = ["dev"] files = [ - {file = "rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0"}, - {file = "rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725"}, + {file = "rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd"}, + {file = "rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4"}, ] [package.dependencies] markdown-it-py = ">=2.2.0" pygments = ">=2.13.0,<3.0.0" -typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""} [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] @@ -1568,7 +1897,7 @@ description = "Python bindings to FreeDesktop.org Secret Service API" optional = false python-versions = ">=3.6" groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" +markers = "(python_full_version < \"3.14.0\" or platform_python_implementation == \"PyPy\") and platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" files = [ {file = "SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"}, {file = "SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"}, @@ -1578,65 +1907,92 @@ files = [ cryptography = ">=2.0" jeepney = ">=0.6" +[[package]] +name = "secretstorage" +version = "3.5.0" +description = "Python bindings to FreeDesktop.org Secret Service API" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +markers = "python_version >= \"3.14\" and platform_python_implementation != \"PyPy\" and platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" +files = [ + {file = "secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137"}, + {file = "secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be"}, +] + +[package.dependencies] +cryptography = ">=2.0" +jeepney = ">=0.6" + [[package]] name = "tomli" -version = "2.2.1" +version = "2.3.0" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" groups = ["dev"] markers = "python_version < \"3.11\"" files = [ - {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, - {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, - {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, - {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, - {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, - {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, - {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, - {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, - {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, - {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, + {file = "tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45"}, + {file = "tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba"}, + {file = "tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf"}, + {file = "tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441"}, + {file = "tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845"}, + {file = "tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c"}, + {file = "tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456"}, + {file = "tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be"}, + {file = "tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac"}, + {file = "tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22"}, + {file = "tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f"}, + {file = "tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52"}, + {file = "tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8"}, + {file = "tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6"}, + {file = "tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876"}, + {file = "tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878"}, + {file = "tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b"}, + {file = "tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae"}, + {file = "tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b"}, + {file = "tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf"}, + {file = "tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f"}, + {file = "tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05"}, + {file = "tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606"}, + {file = "tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999"}, + {file = "tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e"}, + {file = "tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3"}, + {file = "tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc"}, + {file = "tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0"}, + {file = "tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879"}, + {file = "tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005"}, + {file = "tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463"}, + {file = "tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8"}, + {file = "tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77"}, + {file = "tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf"}, + {file = "tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530"}, + {file = "tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b"}, + {file = "tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67"}, + {file = "tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f"}, + {file = "tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0"}, + {file = "tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba"}, + {file = "tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b"}, + {file = "tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549"}, ] [[package]] name = "twine" -version = "6.1.0" +version = "6.2.0" description = "Collection of utilities for publishing packages on PyPI" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "twine-6.1.0-py3-none-any.whl", hash = "sha256:a47f973caf122930bf0fbbf17f80b83bc1602c9ce393c7845f289a3001dc5384"}, - {file = "twine-6.1.0.tar.gz", hash = "sha256:be324f6272eff91d07ee93f251edf232fc647935dd585ac003539b42404a8dbd"}, + {file = "twine-6.2.0-py3-none-any.whl", hash = "sha256:418ebf08ccda9a8caaebe414433b0ba5e25eb5e4a927667122fbe8f829f985d8"}, + {file = "twine-6.2.0.tar.gz", hash = "sha256:e5ed0d2fd70c9959770dce51c8f39c8945c574e18173a7b81802dab51b4b75cf"}, ] [package.dependencies] id = "*" importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""} -keyring = {version = ">=15.1", markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\""} +keyring = {version = ">=21.2.0", markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\""} packaging = ">=24.0" readme-renderer = ">=35.0" requests = ">=2.20" @@ -1646,31 +2002,31 @@ rich = ">=12.0.0" urllib3 = ">=1.26.0" [package.extras] -keyring = ["keyring (>=15.1)"] +keyring = ["keyring (>=21.2.0)"] [[package]] name = "typing-extensions" -version = "4.14.0" +version = "4.15.0" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" groups = ["main", "dev"] files = [ - {file = "typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af"}, - {file = "typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4"}, + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] markers = {main = "python_version < \"3.13\"", dev = "python_version < \"3.11\""} [[package]] name = "urllib3" -version = "2.6.0" +version = "2.6.2" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" groups = ["main", "dev"] files = [ - {file = "urllib3-2.6.0-py3-none-any.whl", hash = "sha256:c90f7a39f716c572c4e3e58509581ebd83f9b59cced005b7db7ad2d22b0db99f"}, - {file = "urllib3-2.6.0.tar.gz", hash = "sha256:cb9bcef5a4b345d5da5d145dc3e30834f58e8018828cbc724d30b4cb7d4d49f1"}, + {file = "urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd"}, + {file = "urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797"}, ] [package.extras] @@ -1698,116 +2054,142 @@ test = ["pytest", "websockets"] [[package]] name = "yarl" -version = "1.20.1" +version = "1.22.0" description = "Yet another URL library" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "yarl-1.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6032e6da6abd41e4acda34d75a816012717000fa6839f37124a47fcefc49bec4"}, - {file = "yarl-1.20.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c7b34d804b8cf9b214f05015c4fee2ebe7ed05cf581e7192c06555c71f4446a"}, - {file = "yarl-1.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c869f2651cc77465f6cd01d938d91a11d9ea5d798738c1dc077f3de0b5e5fed"}, - {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62915e6688eb4d180d93840cda4110995ad50c459bf931b8b3775b37c264af1e"}, - {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:41ebd28167bc6af8abb97fec1a399f412eec5fd61a3ccbe2305a18b84fb4ca73"}, - {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21242b4288a6d56f04ea193adde174b7e347ac46ce6bc84989ff7c1b1ecea84e"}, - {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bea21cdae6c7eb02ba02a475f37463abfe0a01f5d7200121b03e605d6a0439f8"}, - {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f8a891e4a22a89f5dde7862994485e19db246b70bb288d3ce73a34422e55b23"}, - {file = "yarl-1.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd803820d44c8853a109a34e3660e5a61beae12970da479cf44aa2954019bf70"}, - {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b982fa7f74c80d5c0c7b5b38f908971e513380a10fecea528091405f519b9ebb"}, - {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:33f29ecfe0330c570d997bcf1afd304377f2e48f61447f37e846a6058a4d33b2"}, - {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:835ab2cfc74d5eb4a6a528c57f05688099da41cf4957cf08cad38647e4a83b30"}, - {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:46b5e0ccf1943a9a6e766b2c2b8c732c55b34e28be57d8daa2b3c1d1d4009309"}, - {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:df47c55f7d74127d1b11251fe6397d84afdde0d53b90bedb46a23c0e534f9d24"}, - {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76d12524d05841276b0e22573f28d5fbcb67589836772ae9244d90dd7d66aa13"}, - {file = "yarl-1.20.1-cp310-cp310-win32.whl", hash = "sha256:6c4fbf6b02d70e512d7ade4b1f998f237137f1417ab07ec06358ea04f69134f8"}, - {file = "yarl-1.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:aef6c4d69554d44b7f9d923245f8ad9a707d971e6209d51279196d8e8fe1ae16"}, - {file = "yarl-1.20.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47ee6188fea634bdfaeb2cc420f5b3b17332e6225ce88149a17c413c77ff269e"}, - {file = "yarl-1.20.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0f6500f69e8402d513e5eedb77a4e1818691e8f45e6b687147963514d84b44b"}, - {file = "yarl-1.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a8900a42fcdaad568de58887c7b2f602962356908eedb7628eaf6021a6e435b"}, - {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bad6d131fda8ef508b36be3ece16d0902e80b88ea7200f030a0f6c11d9e508d4"}, - {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:df018d92fe22aaebb679a7f89fe0c0f368ec497e3dda6cb81a567610f04501f1"}, - {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f969afbb0a9b63c18d0feecf0db09d164b7a44a053e78a7d05f5df163e43833"}, - {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:812303eb4aa98e302886ccda58d6b099e3576b1b9276161469c25803a8db277d"}, - {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98c4a7d166635147924aa0bf9bfe8d8abad6fffa6102de9c99ea04a1376f91e8"}, - {file = "yarl-1.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12e768f966538e81e6e7550f9086a6236b16e26cd964cf4df35349970f3551cf"}, - {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe41919b9d899661c5c28a8b4b0acf704510b88f27f0934ac7a7bebdd8938d5e"}, - {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8601bc010d1d7780592f3fc1bdc6c72e2b6466ea34569778422943e1a1f3c389"}, - {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:daadbdc1f2a9033a2399c42646fbd46da7992e868a5fe9513860122d7fe7a73f"}, - {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:03aa1e041727cb438ca762628109ef1333498b122e4c76dd858d186a37cec845"}, - {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:642980ef5e0fa1de5fa96d905c7e00cb2c47cb468bfcac5a18c58e27dbf8d8d1"}, - {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:86971e2795584fe8c002356d3b97ef6c61862720eeff03db2a7c86b678d85b3e"}, - {file = "yarl-1.20.1-cp311-cp311-win32.whl", hash = "sha256:597f40615b8d25812f14562699e287f0dcc035d25eb74da72cae043bb884d773"}, - {file = "yarl-1.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:26ef53a9e726e61e9cd1cda6b478f17e350fb5800b4bd1cd9fe81c4d91cfeb2e"}, - {file = "yarl-1.20.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdcc4cd244e58593a4379fe60fdee5ac0331f8eb70320a24d591a3be197b94a9"}, - {file = "yarl-1.20.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b29a2c385a5f5b9c7d9347e5812b6f7ab267193c62d282a540b4fc528c8a9d2a"}, - {file = "yarl-1.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1112ae8154186dfe2de4732197f59c05a83dc814849a5ced892b708033f40dc2"}, - {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90bbd29c4fe234233f7fa2b9b121fb63c321830e5d05b45153a2ca68f7d310ee"}, - {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:680e19c7ce3710ac4cd964e90dad99bf9b5029372ba0c7cbfcd55e54d90ea819"}, - {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a979218c1fdb4246a05efc2cc23859d47c89af463a90b99b7c56094daf25a16"}, - {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255b468adf57b4a7b65d8aad5b5138dce6a0752c139965711bdcb81bc370e1b6"}, - {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a97d67108e79cfe22e2b430d80d7571ae57d19f17cda8bb967057ca8a7bf5bfd"}, - {file = "yarl-1.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8570d998db4ddbfb9a590b185a0a33dbf8aafb831d07a5257b4ec9948df9cb0a"}, - {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:97c75596019baae7c71ccf1d8cc4738bc08134060d0adfcbe5642f778d1dca38"}, - {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1c48912653e63aef91ff988c5432832692ac5a1d8f0fb8a33091520b5bbe19ef"}, - {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4c3ae28f3ae1563c50f3d37f064ddb1511ecc1d5584e88c6b7c63cf7702a6d5f"}, - {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c5e9642f27036283550f5f57dc6156c51084b458570b9d0d96100c8bebb186a8"}, - {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2c26b0c49220d5799f7b22c6838409ee9bc58ee5c95361a4d7831f03cc225b5a"}, - {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:564ab3d517e3d01c408c67f2e5247aad4019dcf1969982aba3974b4093279004"}, - {file = "yarl-1.20.1-cp312-cp312-win32.whl", hash = "sha256:daea0d313868da1cf2fac6b2d3a25c6e3a9e879483244be38c8e6a41f1d876a5"}, - {file = "yarl-1.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:48ea7d7f9be0487339828a4de0360d7ce0efc06524a48e1810f945c45b813698"}, - {file = "yarl-1.20.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0b5ff0fbb7c9f1b1b5ab53330acbfc5247893069e7716840c8e7d5bb7355038a"}, - {file = "yarl-1.20.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:14f326acd845c2b2e2eb38fb1346c94f7f3b01a4f5c788f8144f9b630bfff9a3"}, - {file = "yarl-1.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f60e4ad5db23f0b96e49c018596707c3ae89f5d0bd97f0ad3684bcbad899f1e7"}, - {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49bdd1b8e00ce57e68ba51916e4bb04461746e794e7c4d4bbc42ba2f18297691"}, - {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:66252d780b45189975abfed839616e8fd2dbacbdc262105ad7742c6ae58f3e31"}, - {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59174e7332f5d153d8f7452a102b103e2e74035ad085f404df2e40e663a22b28"}, - {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3968ec7d92a0c0f9ac34d5ecfd03869ec0cab0697c91a45db3fbbd95fe1b653"}, - {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1a4fbb50e14396ba3d375f68bfe02215d8e7bc3ec49da8341fe3157f59d2ff5"}, - {file = "yarl-1.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11a62c839c3a8eac2410e951301309426f368388ff2f33799052787035793b02"}, - {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:041eaa14f73ff5a8986b4388ac6bb43a77f2ea09bf1913df7a35d4646db69e53"}, - {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:377fae2fef158e8fd9d60b4c8751387b8d1fb121d3d0b8e9b0be07d1b41e83dc"}, - {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1c92f4390e407513f619d49319023664643d3339bd5e5a56a3bebe01bc67ec04"}, - {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d25ddcf954df1754ab0f86bb696af765c5bfaba39b74095f27eececa049ef9a4"}, - {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:909313577e9619dcff8c31a0ea2aa0a2a828341d92673015456b3ae492e7317b"}, - {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:793fd0580cb9664548c6b83c63b43c477212c0260891ddf86809e1c06c8b08f1"}, - {file = "yarl-1.20.1-cp313-cp313-win32.whl", hash = "sha256:468f6e40285de5a5b3c44981ca3a319a4b208ccc07d526b20b12aeedcfa654b7"}, - {file = "yarl-1.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:495b4ef2fea40596bfc0affe3837411d6aa3371abcf31aac0ccc4bdd64d4ef5c"}, - {file = "yarl-1.20.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f60233b98423aab21d249a30eb27c389c14929f47be8430efa7dbd91493a729d"}, - {file = "yarl-1.20.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6f3eff4cc3f03d650d8755c6eefc844edde99d641d0dcf4da3ab27141a5f8ddf"}, - {file = "yarl-1.20.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:69ff8439d8ba832d6bed88af2c2b3445977eba9a4588b787b32945871c2444e3"}, - {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cf34efa60eb81dd2645a2e13e00bb98b76c35ab5061a3989c7a70f78c85006d"}, - {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8e0fe9364ad0fddab2688ce72cb7a8e61ea42eff3c7caeeb83874a5d479c896c"}, - {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f64fbf81878ba914562c672024089e3401974a39767747691c65080a67b18c1"}, - {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6342d643bf9a1de97e512e45e4b9560a043347e779a173250824f8b254bd5ce"}, - {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56dac5f452ed25eef0f6e3c6a066c6ab68971d96a9fb441791cad0efba6140d3"}, - {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7d7f497126d65e2cad8dc5f97d34c27b19199b6414a40cb36b52f41b79014be"}, - {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:67e708dfb8e78d8a19169818eeb5c7a80717562de9051bf2413aca8e3696bf16"}, - {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:595c07bc79af2494365cc96ddeb772f76272364ef7c80fb892ef9d0649586513"}, - {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7bdd2f80f4a7df852ab9ab49484a4dee8030023aa536df41f2d922fd57bf023f"}, - {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c03bfebc4ae8d862f853a9757199677ab74ec25424d0ebd68a0027e9c639a390"}, - {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:344d1103e9c1523f32a5ed704d576172d2cabed3122ea90b1d4e11fe17c66458"}, - {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:88cab98aa4e13e1ade8c141daeedd300a4603b7132819c484841bb7af3edce9e"}, - {file = "yarl-1.20.1-cp313-cp313t-win32.whl", hash = "sha256:b121ff6a7cbd4abc28985b6028235491941b9fe8fe226e6fdc539c977ea1739d"}, - {file = "yarl-1.20.1-cp313-cp313t-win_amd64.whl", hash = "sha256:541d050a355bbbc27e55d906bc91cb6fe42f96c01413dd0f4ed5a5240513874f"}, - {file = "yarl-1.20.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e42ba79e2efb6845ebab49c7bf20306c4edf74a0b20fc6b2ccdd1a219d12fad3"}, - {file = "yarl-1.20.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:41493b9b7c312ac448b7f0a42a089dffe1d6e6e981a2d76205801a023ed26a2b"}, - {file = "yarl-1.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f5a5928ff5eb13408c62a968ac90d43f8322fd56d87008b8f9dabf3c0f6ee983"}, - {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30c41ad5d717b3961b2dd785593b67d386b73feca30522048d37298fee981805"}, - {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:59febc3969b0781682b469d4aca1a5cab7505a4f7b85acf6db01fa500fa3f6ba"}, - {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d2b6fb3622b7e5bf7a6e5b679a69326b4279e805ed1699d749739a61d242449e"}, - {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:749d73611db8d26a6281086f859ea7ec08f9c4c56cec864e52028c8b328db723"}, - {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9427925776096e664c39e131447aa20ec738bdd77c049c48ea5200db2237e000"}, - {file = "yarl-1.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff70f32aa316393eaf8222d518ce9118148eddb8a53073c2403863b41033eed5"}, - {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c7ddf7a09f38667aea38801da8b8d6bfe81df767d9dfc8c88eb45827b195cd1c"}, - {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:57edc88517d7fc62b174fcfb2e939fbc486a68315d648d7e74d07fac42cec240"}, - {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:dab096ce479d5894d62c26ff4f699ec9072269d514b4edd630a393223f45a0ee"}, - {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14a85f3bd2d7bb255be7183e5d7d6e70add151a98edf56a770d6140f5d5f4010"}, - {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2c89b5c792685dd9cd3fa9761c1b9f46fc240c2a3265483acc1565769996a3f8"}, - {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:69e9b141de5511021942a6866990aea6d111c9042235de90e08f94cf972ca03d"}, - {file = "yarl-1.20.1-cp39-cp39-win32.whl", hash = "sha256:b5f307337819cdfdbb40193cad84978a029f847b0a357fbe49f712063cfc4f06"}, - {file = "yarl-1.20.1-cp39-cp39-win_amd64.whl", hash = "sha256:eae7bfe2069f9c1c5b05fc7fe5d612e5bbc089a39309904ee8b829e322dcad00"}, - {file = "yarl-1.20.1-py3-none-any.whl", hash = "sha256:83b8eb083fe4683c6115795d9fc1cfaf2cbbefb19b3a1cb68f6527460f483a77"}, - {file = "yarl-1.20.1.tar.gz", hash = "sha256:d017a4997ee50c91fd5466cef416231bb82177b93b029906cefc542ce14c35ac"}, + {file = "yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e"}, + {file = "yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f"}, + {file = "yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467"}, + {file = "yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea"}, + {file = "yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca"}, + {file = "yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e"}, + {file = "yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca"}, + {file = "yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b"}, + {file = "yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520"}, + {file = "yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8"}, + {file = "yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c"}, + {file = "yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67"}, + {file = "yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95"}, + {file = "yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d"}, + {file = "yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62"}, + {file = "yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03"}, + {file = "yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249"}, + {file = "yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da"}, + {file = "yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2"}, + {file = "yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79"}, + {file = "yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c"}, + {file = "yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e"}, + {file = "yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27"}, + {file = "yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3aa27acb6de7a23785d81557577491f6c38a5209a254d1191519d07d8fe51748"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:af74f05666a5e531289cb1cc9c883d1de2088b8e5b4de48004e5ca8a830ac859"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:62441e55958977b8167b2709c164c91a6363e25da322d87ae6dd9c6019ceecf9"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b580e71cac3f8113d3135888770903eaf2f507e9421e5697d6ee6d8cd1c7f054"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e81fda2fb4a07eda1a2252b216aa0df23ebcd4d584894e9612e80999a78fd95b"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:99b6fc1d55782461b78221e95fc357b47ad98b041e8e20f47c1411d0aacddc60"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:088e4e08f033db4be2ccd1f34cf29fe994772fb54cfe004bbf54db320af56890"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e4e1f6f0b4da23e61188676e3ed027ef0baa833a2e633c29ff8530800edccba"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:84fc3ec96fce86ce5aa305eb4aa9358279d1aa644b71fab7b8ed33fe3ba1a7ca"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5dbeefd6ca588b33576a01b0ad58aa934bc1b41ef89dee505bf2932b22ddffba"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14291620375b1060613f4aab9ebf21850058b6b1b438f386cc814813d901c60b"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a4fcfc8eb2c34148c118dfa02e6427ca278bfd0f3df7c5f99e33d2c0e81eae3e"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:029866bde8d7b0878b9c160e72305bbf0a7342bcd20b9999381704ae03308dc8"}, + {file = "yarl-1.22.0-cp39-cp39-win32.whl", hash = "sha256:4dcc74149ccc8bba31ce1944acee24813e93cfdee2acda3c172df844948ddf7b"}, + {file = "yarl-1.22.0-cp39-cp39-win_amd64.whl", hash = "sha256:10619d9fdee46d20edc49d3479e2f8269d0779f1b031e6f7c2aa1c76be04b7ed"}, + {file = "yarl-1.22.0-cp39-cp39-win_arm64.whl", hash = "sha256:dd7afd3f8b0bfb4e0d9fc3c31bfe8a4ec7debe124cfd90619305def3c8ca8cd2"}, + {file = "yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff"}, + {file = "yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71"}, ] [package.dependencies] @@ -1822,7 +2204,7 @@ description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version == \"3.9\"" +markers = "python_version < \"3.12\" and platform_machine != \"ppc64le\" and platform_machine != \"s390x\" or python_version == \"3.9\"" files = [ {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, @@ -1839,4 +2221,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9" -content-hash = "daa72b1d19fdf9cc4af9342c3e985dec611b1bc55c63b95acb36497e59575f3a" +content-hash = "bc511f123580079eb77667e44fb5a8f0595f3183ca7a88fae0b4fc8b36b53240" diff --git a/pyproject.toml b/pyproject.toml index 49d8b03..3fe6b84 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,7 @@ polling2= "0.5.0" black= "25.11.0" flake8= "7.3.0" pytest = ">=8.2.2,<8.5.0" -twine = ">=6.0,<6.2" +twine = ">=6.0" [tool.poetry.scripts] cloudfoundry-client = "cloudfoundry_client.main.main:main" From 0b3c91c224a3a031dc5e9333d13111d8b13668e9 Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Sun, 4 Jan 2026 12:23:05 +0100 Subject: [PATCH 225/264] chore(python) deprecates python 3.9 and add 3.14 in CI (#268) --- .github/workflows/pythonpackage.yml | 2 +- poetry.lock | 173 +++------------------------- pyproject.toml | 2 +- 3 files changed, 16 insertions(+), 161 deletions(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index f8da6ee..cf06cde 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -13,7 +13,7 @@ jobs: strategy: max-parallel: 1 matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] steps: - uses: actions/checkout@v6 diff --git a/poetry.lock b/poetry.lock index ca813d8..184fdfb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -178,7 +178,7 @@ description = "Timeout context manager for asyncio programs" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version < \"3.11\"" +markers = "python_version == \"3.10\"" files = [ {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, @@ -203,7 +203,7 @@ description = "Backport of CPython tarfile module" optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "python_version < \"3.12\" and platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\"" files = [ {file = "backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34"}, {file = "backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991"}, @@ -284,7 +284,7 @@ description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.9" groups = ["dev"] -markers = "platform_python_implementation != \"PyPy\" and platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\"" files = [ {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, {file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"}, @@ -498,22 +498,6 @@ files = [ {file = "charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a"}, ] -[[package]] -name = "click" -version = "8.1.8" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -markers = "python_full_version < \"3.14.0\" or platform_python_implementation == \"PyPy\"" -files = [ - {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, - {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - [[package]] name = "click" version = "8.3.1" @@ -521,7 +505,6 @@ description = "Composable command line interface toolkit" optional = false python-versions = ">=3.10" groups = ["dev"] -markers = "python_version >= \"3.14\" and platform_python_implementation != \"PyPy\"" files = [ {file = "click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6"}, {file = "click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a"}, @@ -543,57 +526,6 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -[[package]] -name = "cryptography" -version = "43.0.3" -description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -optional = false -python-versions = ">=3.7" -groups = ["dev"] -markers = "(python_full_version < \"3.14.0\" or platform_python_implementation == \"PyPy\") and platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" -files = [ - {file = "cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e"}, - {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e"}, - {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f"}, - {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6"}, - {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18"}, - {file = "cryptography-43.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd"}, - {file = "cryptography-43.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73"}, - {file = "cryptography-43.0.3-cp37-abi3-win32.whl", hash = "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2"}, - {file = "cryptography-43.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd"}, - {file = "cryptography-43.0.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984"}, - {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5"}, - {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4"}, - {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7"}, - {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405"}, - {file = "cryptography-43.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16"}, - {file = "cryptography-43.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73"}, - {file = "cryptography-43.0.3-cp39-abi3-win32.whl", hash = "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995"}, - {file = "cryptography-43.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362"}, - {file = "cryptography-43.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d03b5621a135bffecad2c73e9f4deb1a0f977b9a8ffe6f8e002bf6c9d07b918c"}, - {file = "cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a2a431ee15799d6db9fe80c82b055bae5a752bef645bba795e8e52687c69efe3"}, - {file = "cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:281c945d0e28c92ca5e5930664c1cefd85efe80e5c0d2bc58dd63383fda29f83"}, - {file = "cryptography-43.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f18c716be16bc1fea8e95def49edf46b82fccaa88587a45f8dc0ff6ab5d8e0a7"}, - {file = "cryptography-43.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4a02ded6cd4f0a5562a8887df8b3bd14e822a90f97ac5e544c162899bc467664"}, - {file = "cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:53a583b6637ab4c4e3591a15bc9db855b8d9dee9a669b550f311480acab6eb08"}, - {file = "cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1ec0bcf7e17c0c5669d881b1cd38c4972fade441b27bda1051665faaa89bdcaa"}, - {file = "cryptography-43.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2ce6fae5bdad59577b44e4dfed356944fbf1d925269114c28be377692643b4ff"}, - {file = "cryptography-43.0.3.tar.gz", hash = "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805"}, -] - -[package.dependencies] -cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} - -[package.extras] -docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] -docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] -nox = ["nox"] -pep8test = ["check-sdist", "click", "mypy", "ruff"] -sdist = ["build"] -ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi", "cryptography-vectors (==43.0.3)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] -test-randomorder = ["pytest-randomly"] - [[package]] name = "cryptography" version = "46.0.3" @@ -601,7 +533,7 @@ description = "cryptography is a package which provides cryptographic recipes an optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.8" groups = ["dev"] -markers = "python_version >= \"3.14\" and platform_python_implementation != \"PyPy\" and platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" files = [ {file = "cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a"}, {file = "cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc"}, @@ -661,6 +593,7 @@ files = [ [package.dependencies] cffi = {version = ">=2.0.0", markers = "python_full_version >= \"3.9.0\" and platform_python_implementation != \"PyPy\""} +typing-extensions = {version = ">=4.13.2", markers = "python_full_version < \"3.11.0\""} [package.extras] docs = ["sphinx (>=5.3.0)", "sphinx-inline-tabs", "sphinx-rtd-theme (>=3.0.0)"] @@ -691,7 +624,7 @@ description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" groups = ["dev"] -markers = "python_version < \"3.11\"" +markers = "python_version == \"3.10\"" files = [ {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, @@ -902,7 +835,7 @@ description = "Read metadata from Python packages" optional = false python-versions = ">=3.9" groups = ["dev"] -markers = "python_version < \"3.12\" and platform_machine != \"ppc64le\" and platform_machine != \"s390x\" or python_version == \"3.9\"" +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\"" files = [ {file = "importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151"}, {file = "importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb"}, @@ -920,19 +853,6 @@ perf = ["ipython"] test = ["flufl.flake8", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["mypy (<1.19) ; platform_python_implementation == \"PyPy\"", "pytest-mypy (>=1.0.1)"] -[[package]] -name = "iniconfig" -version = "2.1.0" -description = "brain-dead simple config-ini parsing" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -markers = "python_full_version < \"3.14.0\" or platform_python_implementation == \"PyPy\"" -files = [ - {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, - {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, -] - [[package]] name = "iniconfig" version = "2.3.0" @@ -940,7 +860,6 @@ description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.10" groups = ["dev"] -markers = "python_version >= \"3.14\" and platform_python_implementation != \"PyPy\"" files = [ {file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"}, {file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"}, @@ -1062,32 +981,6 @@ enabler = ["pytest-enabler (>=3.4)"] test = ["pyfakefs", "pytest (>=6,!=8.1.*)"] type = ["pygobject-stubs", "pytest-mypy (>=1.0.1)", "shtab", "types-pywin32"] -[[package]] -name = "markdown-it-py" -version = "3.0.0" -description = "Python port of markdown-it. Markdown parsing, done right!" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -markers = "python_full_version < \"3.14.0\" or platform_python_implementation == \"PyPy\"" -files = [ - {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, - {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, -] - -[package.dependencies] -mdurl = ">=0.1,<1.0" - -[package.extras] -benchmarking = ["psutil", "pytest", "pytest-benchmark"] -code-style = ["pre-commit (>=3.0,<4.0)"] -compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] -linkify = ["linkify-it-py (>=1,<3)"] -plugins = ["mdit-py-plugins"] -profiling = ["gprof2dot"] -rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] -testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] - [[package]] name = "markdown-it-py" version = "4.0.0" @@ -1095,7 +988,6 @@ description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.10" groups = ["dev"] -markers = "python_version >= \"3.14\" and platform_python_implementation != \"PyPy\"" files = [ {file = "markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147"}, {file = "markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3"}, @@ -1396,24 +1288,6 @@ files = [ {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] -[[package]] -name = "platformdirs" -version = "4.4.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -markers = "python_full_version < \"3.14.0\" or platform_python_implementation == \"PyPy\"" -files = [ - {file = "platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85"}, - {file = "platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf"}, -] - -[package.extras] -docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] -type = ["mypy (>=1.14.1)"] - [[package]] name = "platformdirs" version = "4.5.1" @@ -1421,7 +1295,6 @@ description = "A small Python package for determining appropriate platform-speci optional = false python-versions = ">=3.10" groups = ["dev"] -markers = "python_version >= \"3.14\" and platform_python_implementation != \"PyPy\"" files = [ {file = "platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31"}, {file = "platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda"}, @@ -1631,7 +1504,7 @@ description = "C parser in Python" optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "platform_python_implementation != \"PyPy\" and platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\" and implementation_name != \"PyPy\"" +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\"" files = [ {file = "pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"}, {file = "pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"}, @@ -1890,23 +1763,6 @@ pygments = ">=2.13.0,<3.0.0" [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] -[[package]] -name = "secretstorage" -version = "3.3.3" -description = "Python bindings to FreeDesktop.org Secret Service API" -optional = false -python-versions = ">=3.6" -groups = ["dev"] -markers = "(python_full_version < \"3.14.0\" or platform_python_implementation == \"PyPy\") and platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" -files = [ - {file = "SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"}, - {file = "SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"}, -] - -[package.dependencies] -cryptography = ">=2.0" -jeepney = ">=0.6" - [[package]] name = "secretstorage" version = "3.5.0" @@ -1914,7 +1770,7 @@ description = "Python bindings to FreeDesktop.org Secret Service API" optional = false python-versions = ">=3.10" groups = ["dev"] -markers = "python_version >= \"3.14\" and platform_python_implementation != \"PyPy\" and platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" files = [ {file = "secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137"}, {file = "secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be"}, @@ -1931,7 +1787,7 @@ description = "A lil' TOML parser" optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "python_version < \"3.11\"" +markers = "python_version == \"3.10\"" files = [ {file = "tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45"}, {file = "tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba"}, @@ -1991,7 +1847,6 @@ files = [ [package.dependencies] id = "*" -importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""} keyring = {version = ">=21.2.0", markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\""} packaging = ">=24.0" readme-renderer = ">=35.0" @@ -2015,7 +1870,7 @@ files = [ {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] -markers = {main = "python_version < \"3.13\"", dev = "python_version < \"3.11\""} +markers = {main = "python_version < \"3.13\"", dev = "python_version == \"3.10\""} [[package]] name = "urllib3" @@ -2204,7 +2059,7 @@ description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" groups = ["dev"] -markers = "python_version < \"3.12\" and platform_machine != \"ppc64le\" and platform_machine != \"s390x\" or python_version == \"3.9\"" +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\"" files = [ {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, @@ -2220,5 +2075,5 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" -python-versions = ">=3.9" -content-hash = "bc511f123580079eb77667e44fb5a8f0595f3183ca7a88fae0b4fc8b36b53240" +python-versions = ">=3.10" +content-hash = "246994f46674747a4a2165687b05deea2c568cd81542206b7a8e5f01cd8d0c95" diff --git a/pyproject.toml b/pyproject.toml index 3fe6b84..639c599 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ repository = "https://github.com/cloudfoundry-community/cf-python-client" keywords = ["cloudfoundry", "cf"] [tool.poetry.dependencies] -python = ">=3.9" +python = ">=3.10" aiohttp = ">=3.8.0" protobuf = "6.33.2" oauth2-client= "1.4.2" From 356e0e2a48aae8653b27760d6ec0b62dbb3f0458 Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Sun, 4 Jan 2026 12:28:05 +0100 Subject: [PATCH 226/264] chore(dependencies) update pytest 9.0.2 (#269) --- poetry.lock | 14 +++++++------- pyproject.toml | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/poetry.lock b/poetry.lock index 184fdfb..dee022f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1539,21 +1539,21 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pytest" -version = "8.4.2" +version = "9.0.2" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"}, - {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"}, + {file = "pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b"}, + {file = "pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11"}, ] [package.dependencies] colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""} -iniconfig = ">=1" -packaging = ">=20" +iniconfig = ">=1.0.1" +packaging = ">=22" pluggy = ">=1.5,<2" pygments = ">=2.7.2" tomli = {version = ">=1", markers = "python_version < \"3.11\""} @@ -2076,4 +2076,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.10" -content-hash = "246994f46674747a4a2165687b05deea2c568cd81542206b7a8e5f01cd8d0c95" +content-hash = "819adce351d38dc60317d81e7985f03e1f0ae1eecc1e647bfb38d297c4889202" diff --git a/pyproject.toml b/pyproject.toml index 639c599..93473fe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ polling2= "0.5.0" [tool.poetry.group.dev.dependencies] black= "25.11.0" flake8= "7.3.0" -pytest = ">=8.2.2,<8.5.0" +pytest = ">=8.2" twine = ">=6.0" [tool.poetry.scripts] From 5c8b2bc14d8e5c4baef90f337252990caf8b8e91 Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Sun, 4 Jan 2026 13:35:58 +0100 Subject: [PATCH 227/264] fixes(orgs-v3) add default None value for 'suspended' update parameter (#270) Closes #261 --- cloudfoundry_client/v3/organizations.py | 13 +++++++++-- tests/v3/test_organizations.py | 30 ++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/cloudfoundry_client/v3/organizations.py b/cloudfoundry_client/v3/organizations.py index 2086d31..47064ff 100644 --- a/cloudfoundry_client/v3/organizations.py +++ b/cloudfoundry_client/v3/organizations.py @@ -20,11 +20,20 @@ def update( self, guid: str, name: str, - suspended: Optional[bool], + suspended: Optional[bool] = None, meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None, ) -> Entity: - data = {"name": name, "suspended": suspended, "metadata": {"labels": meta_labels, "annotations": meta_annotations}} + data = {"name": name} + if suspended is not None: + data["suspended"] = suspended + metadata = {} + if meta_labels is not None: + metadata["labels"] = meta_labels + if meta_annotations is not None: + metadata["annotations"] = meta_annotations + if len(metadata) > 0: + data["metadata"] = metadata return super(OrganizationManager, self)._update(guid, data) def remove(self, guid: str, asynchronous: bool = True) -> Optional[str]: diff --git a/tests/v3/test_organizations.py b/tests/v3/test_organizations.py index 3156850..edc08b4 100644 --- a/tests/v3/test_organizations.py +++ b/tests/v3/test_organizations.py @@ -35,14 +35,38 @@ def test_get(self): self.assertEqual("my-organization", organization["name"]) self.assertIsInstance(organization, Entity) - def test_update(self): + def test_update_without_optional_parameters(self): self.client.patch.return_value = self.mock_response( "/v3/organizations/organization_id", HTTPStatus.OK, None, "v3", "organizations", "PATCH_{id}_response.json" ) - result = self.client.v3.organizations.update("organization_id", "my-organization", suspended=True) + result = self.client.v3.organizations.update("organization_id", "my-organization") self.client.patch.assert_called_with( self.client.patch.return_value.url, - json={"suspended": True, "name": "my-organization", "metadata": {"labels": None, "annotations": None}}, + json={"name": "my-organization"}, + ) + self.assertIsNotNone(result) + + def test_update_with_optional_parameters(self): + self.client.patch.return_value = self.mock_response( + "/v3/organizations/organization_id", HTTPStatus.OK, None, "v3", "organizations", "PATCH_{id}_response.json" + ) + result = self.client.v3.organizations.update( + "organization_id", + "my-organization", + suspended=True, + meta_labels={"label_name": "label_value"}, + meta_annotations={"annotation_name": "annotation_value"} + ) + self.client.patch.assert_called_with( + self.client.patch.return_value.url, + json={ + "suspended": True, + "name": "my-organization", + "metadata": { + "labels": {"label_name": "label_value"}, + "annotations": {"annotation_name": "annotation_value"} + } + }, ) self.assertIsNotNone(result) From 0311a76162ac20e23632d30172df9595bf127552 Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Sun, 4 Jan 2026 13:39:42 +0100 Subject: [PATCH 228/264] Prepare next version --- cloudfoundry_client/__init__.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudfoundry_client/__init__.py b/cloudfoundry_client/__init__.py index 910720d..b3daaa3 100644 --- a/cloudfoundry_client/__init__.py +++ b/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.39.1" +__version__ = "1.39.2" diff --git a/pyproject.toml b/pyproject.toml index 93473fe..e666238 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ exclude = ''' [tool.poetry] name = "cloudfoundry_client" -version = "1.39.1" +version = "1.39.2" description = "A client library for CloudFoundry" authors = ["Benjamin Einaudi "] readme = "README.rst" From d9c547ff0909aaefde184d6193b50ed02c9b8511 Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Tue, 6 Jan 2026 19:08:26 +0100 Subject: [PATCH 229/264] chore(code) remove Optional (#272) --- .github/workflows/pythonpackage.yml | 8 ++-- cloudfoundry_client/client.py | 15 +++--- cloudfoundry_client/common_objects.py | 4 +- .../doppler/websocket_envelope_reader.py | 8 ++-- cloudfoundry_client/networking/entities.py | 14 +++--- .../operations/push/file_helper.py | 4 +- cloudfoundry_client/operations/push/push.py | 4 +- cloudfoundry_client/v2/apps.py | 16 +++---- cloudfoundry_client/v2/entities.py | 20 ++++---- cloudfoundry_client/v2/routes.py | 6 +-- cloudfoundry_client/v2/service_bindings.py | 4 +- cloudfoundry_client/v2/service_brokers.py | 12 ++--- cloudfoundry_client/v2/service_instances.py | 16 +++---- cloudfoundry_client/v2/service_keys.py | 4 +- cloudfoundry_client/v3/apps.py | 4 +- cloudfoundry_client/v3/buildpacks.py | 28 +++++------ cloudfoundry_client/v3/domains.py | 16 +++---- cloudfoundry_client/v3/entities.py | 34 +++++++------- cloudfoundry_client/v3/feature_flags.py | 4 +- cloudfoundry_client/v3/isolation_segments.py | 6 +-- cloudfoundry_client/v3/organization_quotas.py | 32 ++++++------- cloudfoundry_client/v3/organizations.py | 12 ++--- cloudfoundry_client/v3/roles.py | 4 +- cloudfoundry_client/v3/security_groups.py | 46 +++++++++---------- cloudfoundry_client/v3/service_brokers.py | 22 ++++----- .../v3/service_credential_bindings.py | 10 ++-- cloudfoundry_client/v3/service_instances.py | 24 +++++----- cloudfoundry_client/v3/service_offerings.py | 4 +- cloudfoundry_client/v3/service_plans.py | 8 ++-- cloudfoundry_client/v3/spaces.py | 4 +- cloudfoundry_client/v3/tasks.py | 10 ++-- tests/abstract_test_case.py | 3 +- tests/v3/test_apps.py | 8 ++-- 33 files changed, 206 insertions(+), 208 deletions(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index cf06cde..1582906 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -34,8 +34,8 @@ jobs: - name: Install project run: poetry install --no-interaction - - name: Launch tests - run: poetry run pytest - - - name: Launch Linting + - name: Linting run: poetry run flake8 --show-source --statistics + + - name: Tests + run: poetry run pytest diff --git a/cloudfoundry_client/client.py b/cloudfoundry_client/client.py index 6c85d5f..9163f1b 100644 --- a/cloudfoundry_client/client.py +++ b/cloudfoundry_client/client.py @@ -2,7 +2,6 @@ from pathlib import Path import json from http import HTTPStatus -from typing import Optional import requests from oauth2_client.credentials_manager import CredentialManager, ServiceInformation @@ -56,8 +55,8 @@ def __init__( api_v3_url: str, authorization_endpoint: str, api_endpoint: str, - doppler_endpoint: Optional[str], - log_stream_endpoint: Optional[str], + doppler_endpoint: str | None, + log_stream_endpoint: str | None, ): self._api_v2_url = api_v2_url self._api_v3_url = api_v3_url @@ -67,11 +66,11 @@ def __init__( self.log_stream_endpoint = log_stream_endpoint @property - def api_v2_url(self) -> Optional[str]: + def api_v2_url(self) -> str | None: return self._api_v2_url @property - def api_v3_url(self) -> Optional[str]: + def api_v3_url(self) -> str | None: return self._api_v3_url @@ -226,7 +225,7 @@ def rlpgateway(self): else: return self._rlpgateway - def _get_info(self, target_endpoint: str, proxy: Optional[dict] = None, verify: bool = True) -> Info: + def _get_info(self, target_endpoint: str, proxy: dict | None = None, verify: bool = True) -> Info: root_response = CloudFoundryClient._check_response( requests.get("%s/" % target_endpoint, proxies=proxy if proxy is not None else dict(http="", https=""), verify=verify) ) @@ -247,7 +246,7 @@ def _get_info(self, target_endpoint: str, proxy: Optional[dict] = None, verify: ) @staticmethod - def build_from_cf_config(config_path: Optional[str] = None, **kwargs) -> 'CloudFoundryClient': + def build_from_cf_config(config_path: str | None = None, **kwargs) -> 'CloudFoundryClient': config = Path(config_path) if config_path else Path.home() / '.cf/config.json' try: with open(config) as f: @@ -318,7 +317,7 @@ def _grant_client_credentials_request(self) -> dict: client_secret=self.service_information.client_secret, ) - def get(self, url: str, params: Optional[dict] = None, **kwargs) -> Response: + def get(self, url: str, params: dict | None = None, **kwargs) -> Response: response = super(CloudFoundryClient, self).get(url, params, **kwargs) CloudFoundryClient._log_request("GET", url, response) return CloudFoundryClient._check_response(response) diff --git a/cloudfoundry_client/common_objects.py b/cloudfoundry_client/common_objects.py index 36ef6ea..99e9c49 100644 --- a/cloudfoundry_client/common_objects.py +++ b/cloudfoundry_client/common_objects.py @@ -1,5 +1,5 @@ import json -from typing import Callable, TypeVar, Generic, List, Optional +from typing import Callable, TypeVar, Generic, List class Request(dict): @@ -21,7 +21,7 @@ def __init__(self, *args, **kwargs): class Pagination(Generic[ENTITY]): def __init__(self, first_page: JsonObject, total_result: int, - next_page_loader: Callable[[JsonObject], Optional[JsonObject]], + next_page_loader: Callable[[JsonObject], JsonObject | None], resources_accessor: Callable[[JsonObject], List[JsonObject]], instance_creator: Callable[[JsonObject], ENTITY]): self._first_page = first_page diff --git a/cloudfoundry_client/doppler/websocket_envelope_reader.py b/cloudfoundry_client/doppler/websocket_envelope_reader.py index 3bb513e..1a2d77d 100644 --- a/cloudfoundry_client/doppler/websocket_envelope_reader.py +++ b/cloudfoundry_client/doppler/websocket_envelope_reader.py @@ -1,5 +1,5 @@ import ssl -from typing import Callable, Optional, Tuple +from typing import Callable, Tuple import websocket @@ -10,9 +10,9 @@ def __init__( url, access_token_provider: Callable[[], str], verify_ssl: bool = True, - proxy_host: Optional[str] = None, - proxy_port: Optional[int] = None, - proxy_auth: Optional[Tuple[str, str]] = None, + proxy_host: str | None = None, + proxy_port: int | None = None, + proxy_auth: Tuple[str, str] | None = None, ): if not verify_ssl: self._ws = websocket.WebSocket(sslopt=dict(cert_reqs=ssl.CERT_NONE)) diff --git a/cloudfoundry_client/networking/entities.py b/cloudfoundry_client/networking/entities.py index 07d34e7..02fbe09 100644 --- a/cloudfoundry_client/networking/entities.py +++ b/cloudfoundry_client/networking/entities.py @@ -1,6 +1,6 @@ import logging from functools import reduce -from typing import Callable, List, Tuple, Any, Optional, Generator, TYPE_CHECKING +from typing import Callable, List, Tuple, Any, Generator, TYPE_CHECKING from urllib.parse import quote from requests import Response @@ -40,7 +40,7 @@ class EntityManager(object): list_multi_parameters = ["order-by"] def __init__( - self, target_endpoint: str, client: "CloudFoundryClient", entity_uri: str, entity_builder: Optional[EntityBuilder] = None + self, target_endpoint: str, client: "CloudFoundryClient", entity_uri: str, entity_builder: EntityBuilder | None = None ): self.target_endpoint = target_endpoint self.entity_uri = entity_uri @@ -50,7 +50,7 @@ def __init__( ) def _list( - self, requested_path: str, entity_builder: Optional[EntityBuilder] = None, **kwargs + self, requested_path: str, entity_builder: EntityBuilder | None = None, **kwargs ) -> Generator[Entity, None, None]: url_requested = self._get_url_filtered("%s%s" % (self.target_endpoint, requested_path), **kwargs) response = self.client.get(url_requested) @@ -68,7 +68,7 @@ def _remove(self, resource_id: str, **kwargs): url = "%s%s/%s" % (self.target_endpoint, self.entity_uri, resource_id) self._delete(url, **kwargs) - def _post(self, url: str, data: Optional[dict] = None, **kwargs): + def _post(self, url: str, data: dict | None = None, **kwargs): response = self.client.post(url, json=data, **kwargs) _logger.debug("POST - %s - %s", url, response.text) return self._read_response(response) @@ -83,13 +83,13 @@ def __iter__(self) -> Generator[Entity, None, None]: def list(self, **kwargs) -> Generator[Entity, None, None]: return self._list(self.entity_uri, **kwargs) - def get_first(self, **kwargs) -> Optional[Entity]: + def get_first(self, **kwargs) -> Entity | None: kwargs.setdefault("results-per-page", 1) for entity in self._list(self.entity_uri, **kwargs): return entity return None - def _read_response(self, response: Response, other_entity_builder: Optional[EntityBuilder] = None): + def _read_response(self, response: Response, other_entity_builder: EntityBuilder | None = None): entity_builder = self._get_entity_builder(other_entity_builder) result = response.json(object_pairs_hook=JsonObject) return entity_builder(list(result.items())) @@ -98,7 +98,7 @@ def _read_response(self, response: Response, other_entity_builder: Optional[Enti def _request(**mandatory_parameters) -> Request: return Request(**mandatory_parameters) - def _get_entity_builder(self, entity_builder: Optional[EntityBuilder]) -> EntityBuilder: + def _get_entity_builder(self, entity_builder: EntityBuilder | None) -> EntityBuilder: if entity_builder is None: return self.entity_builder else: diff --git a/cloudfoundry_client/operations/push/file_helper.py b/cloudfoundry_client/operations/push/file_helper.py index 713fa0c..d4ac4fd 100644 --- a/cloudfoundry_client/operations/push/file_helper.py +++ b/cloudfoundry_client/operations/push/file_helper.py @@ -2,12 +2,12 @@ import os import stat import zipfile -from typing import Callable, Optional, Generator, Tuple, List +from typing import Callable, Generator, Tuple, List class FileHelper(object): @staticmethod - def zip(file_location: str, directory_path: str, accept: Optional[Callable[[str], bool]] = None): + def zip(file_location: str, directory_path: str, accept: Callable[[str], bool] | None = None): with zipfile.ZipFile(file_location, "w", zipfile.ZIP_DEFLATED) as archive_out: for dir_path, file_names in FileHelper.walk(directory_path): dir_full_location = os.path.join(directory_path, dir_path) diff --git a/cloudfoundry_client/operations/push/push.py b/cloudfoundry_client/operations/push/push.py index c894bd1..b7f0f3b 100644 --- a/cloudfoundry_client/operations/push/push.py +++ b/cloudfoundry_client/operations/push/push.py @@ -5,7 +5,7 @@ import shutil import tempfile import time -from typing import Tuple, List, Dict, Optional +from typing import Tuple, List, Dict from cloudfoundry_client.client import CloudFoundryClient from cloudfoundry_client.operations.push.cf_ignore import CfIgnore @@ -96,7 +96,7 @@ def _build_request_from_manifest(self, app_manifest: dict) -> dict: return request @staticmethod - def _merge_environment(app: Optional[Entity], app_manifest: dict) -> dict: + def _merge_environment(app: Entity | None, app_manifest: dict) -> dict: environment = dict() if app is not None and "environment_json" in app["entity"]: environment.update(app["entity"]["environment_json"]) diff --git a/cloudfoundry_client/v2/apps.py b/cloudfoundry_client/v2/apps.py index cf68ed8..857a76f 100644 --- a/cloudfoundry_client/v2/apps.py +++ b/cloudfoundry_client/v2/apps.py @@ -3,7 +3,7 @@ import os from http import HTTPStatus from time import sleep -from typing import Optional, Dict, TYPE_CHECKING +from typing import Dict, TYPE_CHECKING from cloudfoundry_client.doppler.client import EnvelopeStream from cloudfoundry_client.errors import InvalidStatusCode @@ -107,9 +107,9 @@ def list_service_bindings(self, application_guid: str, **kwargs) -> Pagination[E def start( self, application_guid: str, - check_time: Optional[float] = 0.5, - timeout: Optional[float] = 300.0, - asynchronous: Optional[bool] = False, + check_time: float | None = 0.5, + timeout: float | None = 300.0, + asynchronous: bool | None = False, ) -> Application: result = super(AppManager, self)._update(application_guid, dict(state="STARTED")) if asynchronous: @@ -122,9 +122,9 @@ def start( def stop( self, application_guid: str, - check_time: Optional[float] = 0.5, - timeout: Optional[float] = 500.0, - asynchronous: Optional[bool] = False, + check_time: float | None = 0.5, + timeout: float | None = 500.0, + asynchronous: bool | None = False, ) -> Application: result = super(AppManager, self)._update(application_guid, dict(state="STOPPED")) if asynchronous: @@ -152,7 +152,7 @@ def update(self, application_guid: str, **kwargs) -> Application: def remove(self, application_guid: str): super(AppManager, self)._remove(application_guid) - def upload(self, application_guid: str, resources, application: str, asynchronous: Optional[bool] = False): + def upload(self, application_guid: str, resources, application: str, asynchronous: bool | None = False): application_size = os.path.getsize(application) with open(application, "rb") as binary_file: return self.client.put( diff --git a/cloudfoundry_client/v2/entities.py b/cloudfoundry_client/v2/entities.py index e324d9e..0fac7bf 100644 --- a/cloudfoundry_client/v2/entities.py +++ b/cloudfoundry_client/v2/entities.py @@ -1,5 +1,5 @@ from functools import partial, reduce -from typing import Callable, List, Tuple, Any, Optional, TYPE_CHECKING +from typing import Callable, List, Tuple, Any, TYPE_CHECKING from urllib.parse import quote from requests import Response @@ -50,7 +50,7 @@ class EntityManager(object): timestamp_parameters = ["timestamp"] def __init__( - self, target_endpoint: str, client: "CloudFoundryClient", entity_uri: str, entity_builder: Optional[EntityBuilder] = None + self, target_endpoint: str, client: "CloudFoundryClient", entity_uri: str, entity_builder: EntityBuilder | None = None ): self.target_endpoint = target_endpoint self.entity_uri = entity_uri @@ -59,7 +59,7 @@ def __init__( entity_builder if entity_builder is not None else lambda pairs: Entity(target_endpoint, client, pairs) ) - def _list(self, requested_path: str, entity_builder: Optional[EntityBuilder] = None, **kwargs) -> Pagination[Entity]: + def _list(self, requested_path: str, entity_builder: EntityBuilder | None = None, **kwargs) -> Pagination[Entity]: url_requested = self._get_url_filtered("%s%s" % (self.target_endpoint, requested_path), **kwargs) current_builder = self._get_entity_builder(entity_builder) response_json = self._read_response(self.client.get(url_requested), JsonObject) @@ -68,7 +68,7 @@ def _list(self, requested_path: str, entity_builder: Optional[EntityBuilder] = N lambda page: page["resources"], lambda json_object: current_builder(list(json_object.items()))) - def _next_page(self, current_page: JsonObject) -> Optional[JsonObject]: + def _next_page(self, current_page: JsonObject) -> JsonObject | None: next_url = current_page.get("next_url") if next_url is None: return None @@ -87,16 +87,16 @@ def _remove(self, resource_id: str, **kwargs): url = "%s%s/%s" % (self.target_endpoint, self.entity_uri, resource_id) self._delete(url, **kwargs) - def _get(self, requested_path: str, entity_builder: Optional[EntityBuilder] = None) -> Entity: + def _get(self, requested_path: str, entity_builder: EntityBuilder | None = None) -> Entity: url = "%s%s" % (self.target_endpoint, requested_path) response = self.client.get(url) return self._read_response(response, entity_builder) - def _post(self, url: str, data: Optional[dict] = None, **kwargs): + def _post(self, url: str, data: dict | None = None, **kwargs): response = self.client.post(url, json=data, **kwargs) return self._read_response(response) - def _put(self, url: str, data: Optional[dict] = None, **kwargs): + def _put(self, url: str, data: dict | None = None, **kwargs): response = self.client.put(url, json=data, **kwargs) return self._read_response(response) @@ -112,7 +112,7 @@ def __getitem__(self, entity_guid) -> Entity: def list(self, **kwargs) -> Pagination[Entity]: return self._list(self.entity_uri, **kwargs) - def get_first(self, **kwargs) -> Optional[Entity]: + def get_first(self, **kwargs) -> Entity | None: kwargs.setdefault("results-per-page", 1) for entity in self._list(self.entity_uri, **kwargs): return entity @@ -125,7 +125,7 @@ def get(self, entity_id: str, *extra_paths) -> Entity: requested_path = "%s/%s/%s" % (self.entity_uri, entity_id, "/".join(extra_paths)) return self._get(requested_path) - def _read_response(self, response: Response, other_entity_builder: Optional[EntityBuilder] = None): + def _read_response(self, response: Response, other_entity_builder: EntityBuilder | None = None): entity_builder = self._get_entity_builder(other_entity_builder) result = response.json(object_pairs_hook=JsonObject) return entity_builder(list(result.items())) @@ -134,7 +134,7 @@ def _read_response(self, response: Response, other_entity_builder: Optional[Enti def _request(**mandatory_parameters) -> Request: return Request(**mandatory_parameters) - def _get_entity_builder(self, entity_builder: Optional[EntityBuilder]) -> EntityBuilder: + def _get_entity_builder(self, entity_builder: EntityBuilder | None) -> EntityBuilder: if entity_builder is None: return self.entity_builder else: diff --git a/cloudfoundry_client/v2/routes.py b/cloudfoundry_client/v2/routes.py index 1e4d52c..4fdcf92 100644 --- a/cloudfoundry_client/v2/routes.py +++ b/cloudfoundry_client/v2/routes.py @@ -1,4 +1,4 @@ -from typing import Optional, TYPE_CHECKING +from typing import TYPE_CHECKING from cloudfoundry_client.v2.entities import EntityManager, Entity @@ -10,7 +10,7 @@ class RouteManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(RouteManager, self).__init__(target_endpoint, client, "/v2/routes") - def create_tcp_route(self, domain_guid: str, space_guid: str, port: Optional[int] = None) -> Entity: + def create_tcp_route(self, domain_guid: str, space_guid: str, port: int | None = None) -> Entity: request = self._request(domain_guid=domain_guid, space_guid=space_guid) if port is None: return super(RouteManager, self)._create(request, params=dict(generate_port=True)) @@ -18,6 +18,6 @@ def create_tcp_route(self, domain_guid: str, space_guid: str, port: Optional[int request["port"] = port return super(RouteManager, self)._create(request) - def create_host_route(self, domain_guid: str, space_guid: str, host: str, path: Optional[str] = "") -> Entity: + def create_host_route(self, domain_guid: str, space_guid: str, host: str, path: str | None = "") -> Entity: request = dict(domain_guid=domain_guid, space_guid=space_guid, host=host, path=path) return super(RouteManager, self)._create(request) diff --git a/cloudfoundry_client/v2/service_bindings.py b/cloudfoundry_client/v2/service_bindings.py index 00fc625..21f4e80 100644 --- a/cloudfoundry_client/v2/service_bindings.py +++ b/cloudfoundry_client/v2/service_bindings.py @@ -1,4 +1,4 @@ -from typing import Optional, TYPE_CHECKING +from typing import TYPE_CHECKING from cloudfoundry_client.v2.entities import EntityManager, Entity @@ -10,7 +10,7 @@ class ServiceBindingManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(ServiceBindingManager, self).__init__(target_endpoint, client, "/v2/service_bindings") - def create(self, app_guid: str, instance_guid: str, parameters: Optional[dict] = None, name: Optional[str] = None) -> Entity: + def create(self, app_guid: str, instance_guid: str, parameters: dict | None = None, name: str | None = None) -> Entity: request = self._request(app_guid=app_guid, service_instance_guid=instance_guid) request["parameters"] = parameters request["name"] = name diff --git a/cloudfoundry_client/v2/service_brokers.py b/cloudfoundry_client/v2/service_brokers.py index abe44c7..b29e8b5 100644 --- a/cloudfoundry_client/v2/service_brokers.py +++ b/cloudfoundry_client/v2/service_brokers.py @@ -1,4 +1,4 @@ -from typing import Optional, TYPE_CHECKING +from typing import TYPE_CHECKING from cloudfoundry_client.v2.entities import EntityManager, Entity @@ -11,7 +11,7 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(ServiceBrokerManager, self).__init__(target_endpoint, client, "/v2/service_brokers") def create( - self, broker_url: str, broker_name: str, auth_username: str, auth_password: str, space_guid: Optional[str] = None + self, broker_url: str, broker_name: str, auth_username: str, auth_password: str, space_guid: str | None = None ) -> Entity: request = self._request(broker_url=broker_url, name=broker_name, auth_username=auth_username, auth_password=auth_password) request["space_guid"] = space_guid @@ -20,10 +20,10 @@ def create( def update( self, broker_guid: str, - broker_url: Optional[str] = None, - broker_name: Optional[str] = None, - auth_username: Optional[str] = None, - auth_password: Optional[str] = None, + broker_url: str | None = None, + broker_name: str | None = None, + auth_username: str | None = None, + auth_password: str | None = None, ) -> Entity: request = self._request() request["broker_url"] = broker_url diff --git a/cloudfoundry_client/v2/service_instances.py b/cloudfoundry_client/v2/service_instances.py index f17fa7f..8d6ebbd 100644 --- a/cloudfoundry_client/v2/service_instances.py +++ b/cloudfoundry_client/v2/service_instances.py @@ -1,4 +1,4 @@ -from typing import Optional, List, Dict, TYPE_CHECKING +from typing import List, Dict, TYPE_CHECKING from cloudfoundry_client.v2.entities import EntityManager, Entity @@ -17,9 +17,9 @@ def create( space_guid: str, instance_name: str, plan_guid: str, - parameters: Optional[dict] = None, + parameters: dict | None = None, tags: List[str] = None, - accepts_incomplete: Optional[bool] = False, + accepts_incomplete: bool | None = False, ) -> Entity: request = self._request(name=instance_name, space_guid=space_guid, service_plan_guid=plan_guid) request["parameters"] = parameters @@ -30,11 +30,11 @@ def create( def update( self, instance_guid: str, - instance_name: Optional[str] = None, - plan_guid: Optional[str] = None, - parameters: Optional[dict] = None, + instance_name: str | None = None, + plan_guid: str | None = None, + parameters: dict | None = None, tags: List[str] = None, - accepts_incomplete: Optional[bool] = False, + accepts_incomplete: bool | None = False, ) -> Entity: request = self._request() request["name"] = instance_name @@ -47,7 +47,7 @@ def update( def list_permissions(self, instance_guid: str) -> Dict[str, bool]: return super(ServiceInstanceManager, self)._get("%s/%s/permissions" % (self.entity_uri, instance_guid), dict) - def remove(self, instance_guid: str, accepts_incomplete: Optional[bool] = False, purge: Optional[bool] = False): + def remove(self, instance_guid: str, accepts_incomplete: bool | None = False, purge: bool | None = False): parameters = {} if accepts_incomplete: parameters["accepts_incomplete"] = "true" diff --git a/cloudfoundry_client/v2/service_keys.py b/cloudfoundry_client/v2/service_keys.py index cb87c3e..d807f1a 100644 --- a/cloudfoundry_client/v2/service_keys.py +++ b/cloudfoundry_client/v2/service_keys.py @@ -1,4 +1,4 @@ -from typing import Optional, TYPE_CHECKING +from typing import TYPE_CHECKING from cloudfoundry_client.v2.entities import EntityManager, Entity @@ -10,7 +10,7 @@ class ServiceKeyManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(ServiceKeyManager, self).__init__(target_endpoint, client, "/v2/service_keys") - def create(self, service_instance_guid: str, name: str, parameters: Optional[dict] = None) -> Entity: + def create(self, service_instance_guid: str, name: str, parameters: dict | None = None) -> Entity: request = self._request(service_instance_guid=service_instance_guid, name=name) request["parameters"] = parameters return super(ServiceKeyManager, self)._create(request) diff --git a/cloudfoundry_client/v3/apps.py b/cloudfoundry_client/v3/apps.py index 5ec8486..01746d4 100644 --- a/cloudfoundry_client/v3/apps.py +++ b/cloudfoundry_client/v3/apps.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from cloudfoundry_client.common_objects import JsonObject, Pagination from cloudfoundry_client.v3.entities import EntityManager, Entity @@ -16,7 +16,7 @@ def restart(self, application_guid: str): self.entity_uri, application_guid)) - def remove(self, application_guid: str, asynchronous: bool = True) -> Optional[str]: + def remove(self, application_guid: str, asynchronous: bool = True) -> str | None: return super(AppManager, self)._remove(application_guid, asynchronous) def get_env(self, application_guid: str) -> JsonObject: diff --git a/cloudfoundry_client/v3/buildpacks.py b/cloudfoundry_client/v3/buildpacks.py index f6a1284..ba14c9a 100644 --- a/cloudfoundry_client/v3/buildpacks.py +++ b/cloudfoundry_client/v3/buildpacks.py @@ -1,4 +1,4 @@ -from typing import Optional, TYPE_CHECKING +from typing import TYPE_CHECKING from cloudfoundry_client.v3.entities import EntityManager, Entity @@ -13,12 +13,12 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): def create( self, name: str, - position: Optional[int] = 0, - enabled: Optional[bool] = True, - locked: Optional[bool] = False, - stack: Optional[str] = None, - meta_labels: Optional[dict] = None, - meta_annotations: Optional[dict] = None, + position: int | None = 0, + enabled: bool | None = True, + locked: bool | None = False, + stack: str | None = None, + meta_labels: dict | None = None, + meta_annotations: dict | None = None, ) -> Entity: data = { "name": name, @@ -30,19 +30,19 @@ def create( } return super(BuildpackManager, self)._create(data) - def remove(self, buildpack_guid: str, asynchronous: bool = True) -> Optional[str]: + def remove(self, buildpack_guid: str, asynchronous: bool = True) -> str | None: return super(BuildpackManager, self)._remove(buildpack_guid, asynchronous) def update( self, buildpack_guid: str, name: str, - position: Optional[int] = 0, - enabled: Optional[bool] = True, - locked: Optional[bool] = False, - stack: Optional[str] = None, - meta_labels: Optional[dict] = None, - meta_annotations: Optional[dict] = None, + position: int | None = 0, + enabled: bool | None = True, + locked: bool | None = False, + stack: str | None = None, + meta_labels: dict | None = None, + meta_annotations: dict | None = None, ) -> Entity: data = { "name": name, diff --git a/cloudfoundry_client/v3/domains.py b/cloudfoundry_client/v3/domains.py index d1f5db3..9068cd1 100644 --- a/cloudfoundry_client/v3/domains.py +++ b/cloudfoundry_client/v3/domains.py @@ -1,4 +1,4 @@ -from typing import Optional, TYPE_CHECKING +from typing import TYPE_CHECKING from cloudfoundry_client.common_objects import Pagination from cloudfoundry_client.v3.entities import EntityManager, ToOneRelationship, ToManyRelationship, Entity @@ -26,11 +26,11 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): def create( self, name: str, - internal: Optional[bool] = False, - organization: Optional[ToOneRelationship] = None, - shared_organizations: Optional[ToManyRelationship] = None, - meta_labels: Optional[dict] = None, - meta_annotations: Optional[dict] = None, + internal: bool | None = False, + organization: ToOneRelationship | None = None, + shared_organizations: ToManyRelationship | None = None, + meta_labels: dict | None = None, + meta_annotations: dict | None = None, ) -> Domain: data = { "name": name, @@ -47,11 +47,11 @@ def list_domains_for_org(self, org_guid: str, **kwargs) -> Pagination[Entity]: uri = "/v3/organizations/{guid}/domains".format(guid=org_guid) return self._list(uri, **kwargs) - def update(self, domain_guid: str, meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Domain: + def update(self, domain_guid: str, meta_labels: dict | None = None, meta_annotations: dict | None = None) -> Domain: data = {"metadata": {"labels": meta_labels, "annotations": meta_annotations}} return super(DomainManager, self)._update(domain_guid, data) - def remove(self, domain_guid: str, asynchronous: bool = True) -> Optional[str]: + def remove(self, domain_guid: str, asynchronous: bool = True) -> str | None: return super(DomainManager, self)._remove(domain_guid, asynchronous) def __create_shared_domain_url(self, domain_guid: str) -> str: diff --git a/cloudfoundry_client/v3/entities.py b/cloudfoundry_client/v3/entities.py index 7fa7d23..de4b8b4 100644 --- a/cloudfoundry_client/v3/entities.py +++ b/cloudfoundry_client/v3/entities.py @@ -1,6 +1,6 @@ import functools from json import JSONDecodeError -from typing import Any, Optional, List, Tuple, Union, TypeVar, TYPE_CHECKING, Callable, Type +from typing import Any, List, Tuple, Union, TypeVar, TYPE_CHECKING, Callable, Type from urllib.parse import quote, urlparse from requests import Response @@ -63,7 +63,7 @@ def default_method(m, u): return default_method @staticmethod - def _manager_method(link_name: str, link_method: str) -> Optional[str]: + def _manager_method(link_name: str, link_method: str) -> str | None: if link_method == "get": if link_name.endswith("s"): return "_attempt_to_paginate" @@ -79,7 +79,7 @@ def _manager_method(link_name: str, link_method: str) -> Optional[str]: class Relationship(JsonObject): - def __init__(self, guid: Optional[str]): + def __init__(self, guid: str | None): super(Relationship, self).__init__(guid=guid) @@ -93,7 +93,7 @@ def from_json_object(to_one_relationship: JsonObject): result.update(to_one_relationship) return result - def __init__(self, guid: Optional[str]): + def __init__(self, guid: str | None): super(ToOneRelationship, self).__init__(data=Relationship(guid)) self.guid = guid @@ -120,24 +120,24 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient", entity_ur self.client = client self.entity_type = entity_type - def _post(self, url: str, data: Optional[dict] = None, files: Any = None, entity_type: ENTITY_TYPE = None) -> Entity: + def _post(self, url: str, data: dict | None = None, files: Any = None, entity_type: ENTITY_TYPE = None) -> Entity: response = self.client.post(url, json=data, files=files) return self._read_response(response, entity_type) - def _get(self, url: str, entity_type: Optional[ENTITY_TYPE] = None, **kwargs) -> Entity: + def _get(self, url: str, entity_type: ENTITY_TYPE | None = None, **kwargs) -> Entity: url_requested = EntityManager._get_url_with_encoded_params(url, **kwargs) response = self.client.get(url_requested) return self._read_response(response, entity_type) - def _put(self, url: str, data: dict, entity_type: Optional[ENTITY_TYPE] = None) -> Entity: + def _put(self, url: str, data: dict, entity_type: ENTITY_TYPE | None = None) -> Entity: response = self.client.put(url, json=data) return self._read_response(response, entity_type) - def _patch(self, url: str, data: dict, entity_type: Optional[ENTITY_TYPE] = None) -> Entity: + def _patch(self, url: str, data: dict, entity_type: ENTITY_TYPE | None = None) -> Entity: response = self.client.patch(url, json=data) return self._read_response(response, entity_type) - def _delete(self, url: str) -> Optional[str]: + def _delete(self, url: str) -> str | None: response = self.client.delete(url) return self._location(response) @@ -148,7 +148,7 @@ def _location(response): except (AttributeError, KeyError): return None - def _remove(self, resource_id: str, asynchronous: bool = True) -> Optional[str]: + def _remove(self, resource_id: str, asynchronous: bool = True) -> str | None: url = "%s%s/%s" % (self.target_endpoint, self.entity_uri, resource_id) job_location = self._delete(url) if job_location is not None: @@ -165,12 +165,12 @@ def _extract_job_guid(job_location): job_guid = job_url.path.rsplit("/", 1)[-1] return job_guid - def _list(self, requested_path: str, entity_type: Optional[ENTITY_TYPE] = None, **kwargs) -> Pagination[Entity]: + def _list(self, requested_path: str, entity_type: ENTITY_TYPE | None = None, **kwargs) -> Pagination[Entity]: url_requested = EntityManager._get_url_with_encoded_params("%s%s" % (self.target_endpoint, requested_path), **kwargs) response_json = self._read_response(self.client.get(url_requested), JsonObject) return self._pagination(response_json, entity_type) - def _attempt_to_paginate(self, url_requested: str, entity_type: Optional[ENTITY_TYPE] = None) \ + def _attempt_to_paginate(self, url_requested: str, entity_type: ENTITY_TYPE | None = None) \ -> Union[Pagination[Entity], Entity]: response_json = self._read_response(self.client.get(url_requested), JsonObject) if "resources" in response_json: @@ -178,7 +178,7 @@ def _attempt_to_paginate(self, url_requested: str, entity_type: Optional[ENTITY_ else: return response_json - def _pagination(self, page: JsonObject, entity_type: Optional[ENTITY_TYPE] = None) -> Pagination[Entity]: + def _pagination(self, page: JsonObject, entity_type: ENTITY_TYPE | None = None) -> Pagination[Entity]: def _entity(json_object: JsonObject) -> Entity: return self._entity(json_object, entity_type) @@ -188,7 +188,7 @@ def _entity(json_object: JsonObject) -> Entity: lambda p: p["resources"], _entity) - def _next_page(self, current_page: JsonObject) -> Optional[JsonObject]: + def _next_page(self, current_page: JsonObject) -> JsonObject | None: pagination = current_page.get("pagination") if ( pagination is None @@ -233,7 +233,7 @@ def len(self, **kwargs): def list(self, **kwargs) -> Pagination[Entity]: return self._list(self.entity_uri, **kwargs) - def get_first(self, **kwargs) -> Optional[Entity]: + def get_first(self, **kwargs) -> Entity | None: kwargs.setdefault("per_page", 1) for entity in self._list(self.entity_uri, **kwargs): return entity @@ -246,7 +246,7 @@ def get(self, entity_id: str, *extra_paths, **kwargs) -> Entity: requested_path = "%s%s/%s/%s" % (self.target_endpoint, self.entity_uri, entity_id, "/".join(extra_paths)) return self._get(requested_path, **kwargs) - def _read_response(self, response: Response, entity_type: Optional[ENTITY_TYPE]) -> Union[JsonObject, Entity]: + def _read_response(self, response: Response, entity_type: ENTITY_TYPE | None) -> Union[JsonObject, Entity]: try: result = response.json(object_pairs_hook=JsonObject) except JSONDecodeError: @@ -288,7 +288,7 @@ def _include_resources(self, resource: JsonObject, result: JsonObject) -> None: def _get_entity_type(entity_name: str) -> Type[ENTITY_TYPE]: return Entity - def _entity(self, result: JsonObject, entity_type: Optional[ENTITY_TYPE]) -> Union[JsonObject, Entity]: + def _entity(self, result: JsonObject, entity_type: ENTITY_TYPE | None) -> Union[JsonObject, Entity]: if "guid" in result or ("links" in result and "job" in result["links"]): return (entity_type or self.entity_type)(self.target_endpoint, self.client, **result) else: diff --git a/cloudfoundry_client/v3/feature_flags.py b/cloudfoundry_client/v3/feature_flags.py index 47c35d8..c75517d 100644 --- a/cloudfoundry_client/v3/feature_flags.py +++ b/cloudfoundry_client/v3/feature_flags.py @@ -1,4 +1,4 @@ -from typing import Optional, TYPE_CHECKING +from typing import TYPE_CHECKING from cloudfoundry_client.v3.entities import EntityManager, Entity @@ -10,6 +10,6 @@ class FeatureFlagManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(FeatureFlagManager, self).__init__(target_endpoint, client, "/v3/feature_flags") - def update(self, name: str, enabled: Optional[bool] = True, custom_error_message: Optional[str] = None) -> Entity: + def update(self, name: str, enabled: bool | None = True, custom_error_message: str | None = None) -> Entity: data = {"enabled": enabled, "custom_error_message": custom_error_message} return super(FeatureFlagManager, self)._update(name, data) diff --git a/cloudfoundry_client/v3/isolation_segments.py b/cloudfoundry_client/v3/isolation_segments.py index 67fa114..e11edb9 100644 --- a/cloudfoundry_client/v3/isolation_segments.py +++ b/cloudfoundry_client/v3/isolation_segments.py @@ -1,4 +1,4 @@ -from typing import Optional, TYPE_CHECKING +from typing import TYPE_CHECKING from cloudfoundry_client.v3.entities import EntityManager, Entity, ToManyRelationship @@ -10,12 +10,12 @@ class IsolationSegmentManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(IsolationSegmentManager, self).__init__(target_endpoint, client, "/v3/isolation_segments") - def create(self, name: str, meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Entity: + def create(self, name: str, meta_labels: dict | None = None, meta_annotations: dict | None = None) -> Entity: data = {"name": name, "metadata": {"labels": meta_labels, "annotations": meta_annotations}} return super(IsolationSegmentManager, self)._create(data) def update( - self, isolation_segment_guid: str, name: str, meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None + self, isolation_segment_guid: str, name: str, meta_labels: dict | None = None, meta_annotations: dict | None = None ) -> Entity: data = {"name": name, "metadata": {"labels": meta_labels, "annotations": meta_annotations}} return super(IsolationSegmentManager, self)._update(isolation_segment_guid, data) diff --git a/cloudfoundry_client/v3/organization_quotas.py b/cloudfoundry_client/v3/organization_quotas.py index abe5015..51f4b56 100644 --- a/cloudfoundry_client/v3/organization_quotas.py +++ b/cloudfoundry_client/v3/organization_quotas.py @@ -1,4 +1,4 @@ -from typing import Optional, TYPE_CHECKING +from typing import TYPE_CHECKING from dataclasses import dataclass, asdict from cloudfoundry_client.v3.entities import Entity, EntityManager, ToManyRelationship @@ -37,17 +37,17 @@ class OrganizationQuotaManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super().__init__(target_endpoint, client, "/v3/organization_quotas") - def remove(self, guid: str, asynchronous: bool = True) -> Optional[str]: + def remove(self, guid: str, asynchronous: bool = True) -> str | None: return super()._remove(guid, asynchronous) def create( self, name: str, - apps_quota: Optional[AppsQuota] = None, - services_quota: Optional[ServicesQuota] = None, - routes_quota: Optional[RoutesQuota] = None, - domains_quota: Optional[DomainsQuota] = None, - assigned_organizations: Optional[ToManyRelationship] = None, + apps_quota: AppsQuota | None = None, + services_quota: ServicesQuota | None = None, + routes_quota: RoutesQuota | None = None, + domains_quota: DomainsQuota | None = None, + assigned_organizations: ToManyRelationship | None = None, ) -> Entity: data = self._asdict(name, apps_quota, services_quota, routes_quota, domains_quota, assigned_organizations) return super()._create(data) @@ -56,10 +56,10 @@ def update( self, guid: str, name: str, - apps_quota: Optional[AppsQuota] = None, - services_quota: Optional[ServicesQuota] = None, - routes_quota: Optional[RoutesQuota] = None, - domains_quota: Optional[DomainsQuota] = None, + apps_quota: AppsQuota | None = None, + services_quota: ServicesQuota | None = None, + routes_quota: RoutesQuota | None = None, + domains_quota: DomainsQuota | None = None, ) -> Entity: data = self._asdict(name, apps_quota, services_quota, routes_quota, domains_quota) return super()._update(guid, data) @@ -74,11 +74,11 @@ def apply_to_organizations(self, guid: str, organizations: ToManyRelationship) - def _asdict( self, name: str, - apps_quota: Optional[AppsQuota] = None, - services_quota: Optional[ServicesQuota] = None, - routes_quota: Optional[RoutesQuota] = None, - domains_quota: Optional[DomainsQuota] = None, - assigned_organizations: Optional[ToManyRelationship] = None, + apps_quota: AppsQuota | None = None, + services_quota: ServicesQuota | None = None, + routes_quota: RoutesQuota | None = None, + domains_quota: DomainsQuota | None = None, + assigned_organizations: ToManyRelationship | None = None, ): data = {"name": name} if apps_quota: diff --git a/cloudfoundry_client/v3/organizations.py b/cloudfoundry_client/v3/organizations.py index 47064ff..252411d 100644 --- a/cloudfoundry_client/v3/organizations.py +++ b/cloudfoundry_client/v3/organizations.py @@ -1,4 +1,4 @@ -from typing import Optional, TYPE_CHECKING +from typing import TYPE_CHECKING from cloudfoundry_client.v3.entities import EntityManager, Entity, ToOneRelationship @@ -11,7 +11,7 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(OrganizationManager, self).__init__(target_endpoint, client, "/v3/organizations") def create( - self, name: str, suspended: bool, meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None + self, name: str, suspended: bool, meta_labels: dict | None = None, meta_annotations: dict | None = None ) -> Entity: data = {"name": name, "suspended": suspended, "metadata": {"labels": meta_labels, "annotations": meta_annotations}} return super(OrganizationManager, self)._create(data) @@ -20,9 +20,9 @@ def update( self, guid: str, name: str, - suspended: Optional[bool] = None, - meta_labels: Optional[dict] = None, - meta_annotations: Optional[dict] = None, + suspended: bool | None = None, + meta_labels: dict | None = None, + meta_annotations: dict | None = None, ) -> Entity: data = {"name": name} if suspended is not None: @@ -36,7 +36,7 @@ def update( data["metadata"] = metadata return super(OrganizationManager, self)._update(guid, data) - def remove(self, guid: str, asynchronous: bool = True) -> Optional[str]: + def remove(self, guid: str, asynchronous: bool = True) -> str | None: return super(OrganizationManager, self)._remove(guid, asynchronous) def assign_default_isolation_segment(self, org_guid: str, iso_seg_guid: str) -> Entity: diff --git a/cloudfoundry_client/v3/roles.py b/cloudfoundry_client/v3/roles.py index 9123d5a..d7f00c2 100644 --- a/cloudfoundry_client/v3/roles.py +++ b/cloudfoundry_client/v3/roles.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from cloudfoundry_client.v3.entities import EntityManager @@ -10,5 +10,5 @@ class RoleManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(RoleManager, self).__init__(target_endpoint, client, "/v3/roles") - def remove(self, role_guid: str, asynchronous: bool = True) -> Optional[str]: + def remove(self, role_guid: str, asynchronous: bool = True) -> str | None: return super(RoleManager, self)._remove(role_guid, asynchronous) diff --git a/cloudfoundry_client/v3/security_groups.py b/cloudfoundry_client/v3/security_groups.py index b134e03..43c32bc 100644 --- a/cloudfoundry_client/v3/security_groups.py +++ b/cloudfoundry_client/v3/security_groups.py @@ -1,6 +1,6 @@ from dataclasses import dataclass, asdict from enum import Enum, auto -from typing import TYPE_CHECKING, Optional, List +from typing import TYPE_CHECKING, List from cloudfoundry_client.v3.entities import EntityManager, ToManyRelationship, Entity, ToOneRelationship @@ -22,17 +22,17 @@ def __repr__(self): class Rule: protocol: RuleProtocol destination: str - ports: Optional[str] = None - type: Optional[int] = None - code: Optional[int] = None - description: Optional[str] = None - log: Optional[bool] = None + ports: str | None = None + type: int | None = None + code: int | None = None + description: str | None = None + log: bool | None = None @dataclass class GloballyEnabled: - running: Optional[bool] = None - staging: Optional[bool] = None + running: bool | None = None + staging: bool | None = None class SecurityGroupManager(EntityManager): @@ -41,24 +41,24 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): def create(self, name: str, - rules: Optional[List[Rule]] = None, - globally_enabled: Optional[GloballyEnabled] = None, - staging_spaces: Optional[ToManyRelationship] = None, - running_spaces: Optional[ToManyRelationship] = None) -> Entity: + rules: List[Rule] | None = None, + globally_enabled: GloballyEnabled | None = None, + staging_spaces: ToManyRelationship | None = None, + running_spaces: ToManyRelationship | None = None) -> Entity: payload = self._generate_payload(name, rules, globally_enabled, staging_spaces, running_spaces) return super()._create(payload) def update(self, security_group_id: str, - name: Optional[str] = None, - rules: Optional[List[Rule]] = None, - globally_enabled: Optional[GloballyEnabled] = None, - staging_spaces: Optional[ToManyRelationship] = None, - running_spaces: Optional[ToManyRelationship] = None) -> Entity: + name: str | None = None, + rules: List[Rule] | None = None, + globally_enabled: GloballyEnabled | None = None, + staging_spaces: ToManyRelationship | None = None, + running_spaces: ToManyRelationship | None = None) -> Entity: payload = self._generate_payload(name, rules, globally_enabled, staging_spaces, running_spaces) return super()._update(security_group_id, payload) - def remove(self, security_group_id: str, asynchronous: bool = True) -> Optional[str]: + def remove(self, security_group_id: str, asynchronous: bool = True) -> str | None: return super()._remove(security_group_id, asynchronous) def bind_running_security_group_to_spaces(self, security_group_id: str, space_guids: ToManyRelationship) \ @@ -90,11 +90,11 @@ def _unbind_space(self, security_group_id: str, space_guid: ToOneRelationship, r super()._delete(url) @staticmethod - def _generate_payload(name: Optional[str], - rules: Optional[List[Rule]], - globally_enabled: Optional[GloballyEnabled], - staging_spaces: Optional[ToManyRelationship], - running_spaces: Optional[ToManyRelationship]): + def _generate_payload(name: str | None, + rules: List[Rule] | None, + globally_enabled: GloballyEnabled | None, + staging_spaces: ToManyRelationship | None, + running_spaces: ToManyRelationship | None): payload = {} if name: payload["name"] = name diff --git a/cloudfoundry_client/v3/service_brokers.py b/cloudfoundry_client/v3/service_brokers.py index c39795b..f1febc6 100644 --- a/cloudfoundry_client/v3/service_brokers.py +++ b/cloudfoundry_client/v3/service_brokers.py @@ -1,4 +1,4 @@ -from typing import Optional, TYPE_CHECKING +from typing import TYPE_CHECKING from cloudfoundry_client.v3.entities import EntityManager, Entity, ToOneRelationship @@ -16,9 +16,9 @@ def create( url: str, auth_username: str, auth_password: str, - space_guid: Optional[str] = None, - meta_labels: Optional[dict] = None, - meta_annotations: Optional[dict] = None, + space_guid: str | None = None, + meta_labels: dict | None = None, + meta_annotations: dict | None = None, ) -> Entity: credentials = {"type": "basic", "credentials": {"username": auth_username, "password": auth_password}} payload = dict(name=name, url=url, authentication=credentials) @@ -36,12 +36,12 @@ def create( def update( self, guid: str, - name: Optional[str] = None, - url: Optional[str] = None, - auth_username: Optional[str] = None, - auth_password: Optional[str] = None, - meta_labels: Optional[dict] = None, - meta_annotations: Optional[dict] = None, + name: str | None = None, + url: str | None = None, + auth_username: str | None = None, + auth_password: str | None = None, + meta_labels: dict | None = None, + meta_annotations: dict | None = None, ) -> Entity: payload = dict() if name: @@ -59,5 +59,5 @@ def update( payload["metadata"] = metadata return super(ServiceBrokerManager, self)._update(guid, payload) - def remove(self, guid: str, asynchronous: bool = True) -> Optional[str]: + def remove(self, guid: str, asynchronous: bool = True) -> str | None: return super(ServiceBrokerManager, self)._remove(guid, asynchronous) diff --git a/cloudfoundry_client/v3/service_credential_bindings.py b/cloudfoundry_client/v3/service_credential_bindings.py index d587b7a..b380b90 100644 --- a/cloudfoundry_client/v3/service_credential_bindings.py +++ b/cloudfoundry_client/v3/service_credential_bindings.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Optional, Union +from typing import TYPE_CHECKING, Union from cloudfoundry_client.v3.entities import EntityManager, Entity, ToOneRelationship @@ -16,10 +16,10 @@ def create( name: str, service_credential_binding_type: str, service_instance_guid: str, - application_guid: Optional[str], - parameters: Optional[dict], - meta_labels: Optional[dict], - meta_annotations: Optional[dict], + application_guid: str | None, + parameters: dict | None, + meta_labels: dict | None, + meta_annotations: dict | None, asynchronous: bool = True, ) -> Union[str, Entity, None]: data = { diff --git a/cloudfoundry_client/v3/service_instances.py b/cloudfoundry_client/v3/service_instances.py index ad33614..cfa253f 100644 --- a/cloudfoundry_client/v3/service_instances.py +++ b/cloudfoundry_client/v3/service_instances.py @@ -1,4 +1,4 @@ -from typing import Optional, TYPE_CHECKING, List +from typing import TYPE_CHECKING, List from cloudfoundry_client.common_objects import JsonObject from cloudfoundry_client.v3.entities import Entity, EntityManager, ToOneRelationship @@ -16,10 +16,10 @@ def create( name: str, space_guid: str, service_plan_guid: str, - meta_labels: Optional[dict] = None, - meta_annotations: Optional[dict] = None, - parameters: Optional[dict] = None, - tags: Optional[List[str]] = None, + meta_labels: dict | None = None, + meta_annotations: dict | None = None, + parameters: dict | None = None, + tags: List[str] | None = None, ) -> Entity: data = { "name": name, @@ -42,13 +42,13 @@ def create( def update( self, instance_guid: str, - name: Optional[str] = None, - parameters: Optional[dict] = None, - service_plan: Optional[str] = None, - maintenance_info: Optional[str] = None, - meta_labels: Optional[dict] = None, - meta_annotations: Optional[dict] = None, - tags: Optional[List[str]] = None + name: str | None = None, + parameters: dict | None = None, + service_plan: str | None = None, + maintenance_info: str | None = None, + meta_labels: dict | None = None, + meta_annotations: dict | None = None, + tags: List[str] | None = None ) -> Entity: data = {} if name: diff --git a/cloudfoundry_client/v3/service_offerings.py b/cloudfoundry_client/v3/service_offerings.py index 37f7d0c..c47ca66 100644 --- a/cloudfoundry_client/v3/service_offerings.py +++ b/cloudfoundry_client/v3/service_offerings.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from cloudfoundry_client.v3.entities import EntityManager, Entity @@ -10,7 +10,7 @@ class ServiceOfferingsManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(ServiceOfferingsManager, self).__init__(target_endpoint, client, "/v3/service_offerings") - def update(self, guid: str, meta_labels: Optional[dict] = None, meta_annotations: Optional[dict] = None) -> Entity: + def update(self, guid: str, meta_labels: dict | None = None, meta_annotations: dict | None = None) -> Entity: payload = dict() if meta_labels or meta_annotations: metadata = dict() diff --git a/cloudfoundry_client/v3/service_plans.py b/cloudfoundry_client/v3/service_plans.py index 257c3bf..0534cec 100644 --- a/cloudfoundry_client/v3/service_plans.py +++ b/cloudfoundry_client/v3/service_plans.py @@ -1,4 +1,4 @@ -from typing import Dict, List, Optional, TYPE_CHECKING +from typing import Dict, List, TYPE_CHECKING from cloudfoundry_client.v3.entities import EntityManager, Entity @@ -13,8 +13,8 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): def update( self, guid: str, - meta_labels: Optional[dict] = None, - meta_annotations: Optional[dict] = None, + meta_labels: dict | None = None, + meta_annotations: dict | None = None, ) -> Entity: payload = {"metadata": {}} @@ -32,7 +32,7 @@ def get_visibility(self, service_plan_guid: str) -> Dict: # Updates a service plan visibility. It behaves similar to the POST service plan visibility endpoint but # this endpoint will REPLACE the existing list of organizations when the service plan is organization visible. - def update_visibility(self, service_plan_guid: str, type: str, organizations: Optional[List[dict]] = None) -> Dict: + def update_visibility(self, service_plan_guid: str, type: str, organizations: List[dict] | None = None) -> Dict: payload = {"type": type} if organizations: payload["organizations"] = organizations diff --git a/cloudfoundry_client/v3/spaces.py b/cloudfoundry_client/v3/spaces.py index f871a43..fd78d68 100644 --- a/cloudfoundry_client/v3/spaces.py +++ b/cloudfoundry_client/v3/spaces.py @@ -1,4 +1,4 @@ -from typing import Optional, TYPE_CHECKING +from typing import TYPE_CHECKING from cloudfoundry_client.v3.entities import EntityManager, ToOneRelationship, Entity @@ -23,7 +23,7 @@ def get_assigned_isolation_segment(self, space_guid: str) -> ToOneRelationship: ) ) - def assign_isolation_segment(self, space_guid: str, isolation_segment_guid: Optional[str]) -> ToOneRelationship: + def assign_isolation_segment(self, space_guid: str, isolation_segment_guid: str | None) -> ToOneRelationship: return ToOneRelationship.from_json_object( super(SpaceManager, self)._patch( "%s%s/%s/relationships/isolation_segment" % (self.target_endpoint, self.entity_uri, space_guid), diff --git a/cloudfoundry_client/v3/tasks.py b/cloudfoundry_client/v3/tasks.py index a7ce16e..c5b23d5 100644 --- a/cloudfoundry_client/v3/tasks.py +++ b/cloudfoundry_client/v3/tasks.py @@ -1,4 +1,4 @@ -from typing import Optional, TYPE_CHECKING +from typing import TYPE_CHECKING from cloudfoundry_client.v3.entities import EntityManager, Entity @@ -14,10 +14,10 @@ def create( self, application_guid: str, command: str, - name: Optional[str] = None, - disk_in_mb: Optional[int] = None, - memory_in_mb: Optional[int] = None, - droplet_guid: Optional[str] = None, + name: str | None = None, + disk_in_mb: int | None = None, + memory_in_mb: int | None = None, + droplet_guid: str | None = None, ) -> Entity: request = self._request(command=command) request["name"] = name diff --git a/tests/abstract_test_case.py b/tests/abstract_test_case.py index 975fc2e..b3c737f 100644 --- a/tests/abstract_test_case.py +++ b/tests/abstract_test_case.py @@ -1,7 +1,6 @@ import json import os from http import HTTPStatus -from typing import Optional from unittest.mock import MagicMock, patch from oauth2_client.credentials_manager import CredentialManager @@ -83,7 +82,7 @@ def get_fixtures_path(*paths): return os.path.join(os.path.dirname(__file__), "fixtures", *paths) @staticmethod - def mock_response(uri: str, status_code: HTTPStatus, headers: Optional[dict], *path_parts: str): + def mock_response(uri: str, status_code: HTTPStatus, headers: dict | None, *path_parts: str): if len(path_parts) > 0: file_name = path_parts[len(path_parts) - 1] extension_idx = file_name.rfind(".") diff --git a/tests/v3/test_apps.py b/tests/v3/test_apps.py index 5c9ed9d..3b51108 100644 --- a/tests/v3/test_apps.py +++ b/tests/v3/test_apps.py @@ -1,7 +1,7 @@ import unittest import yaml from http import HTTPStatus -from typing import Optional, List, Union +from typing import List, Union from abstract_test_case import AbstractTestCase from cloudfoundry_client.common_objects import JsonObject, Pagination @@ -143,17 +143,17 @@ def test_get_manifest(self): manifest_response: str = self.client.v3.apps.get_manifest("app_id") self.assertIsInstance(manifest_response, str) manifest: dict = yaml.safe_load(manifest_response) - applications: Optional[list[dict]] = manifest.get("applications") + applications: list[dict] | None = manifest.get("applications") self.assertIsInstance(applications, list) self.assertEqual(len(applications), 1) application: dict = applications[0] self.assertEqual(application.get("name"), "my-app") self.assertEqual(application.get("stack"), "cflinuxfs4") - application_services: Optional[list[str]] = application.get("services") + application_services: list[str] | None = application.get("services") self.assertIsInstance(application_services, list) self.assertEqual(len(application_services), 1) self.assertEqual(application_services[0], "my-service") - application_routes: Optional[List[Union[dict, str]]] = application.get("routes") + application_routes: List[Union[dict, str]] | None = application.get("routes") self.assertIsInstance(application_routes, list) self.assertEqual(len(application_routes), 1) application_route: dict = application_routes[0] From 29e7bbaf249ba5b44b28fb469f4a2bbcf84f9557 Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Tue, 6 Jan 2026 19:17:06 +0100 Subject: [PATCH 230/264] chore(code) remove Union (#273) --- cloudfoundry_client/v3/entities.py | 8 ++++---- cloudfoundry_client/v3/service_credential_bindings.py | 4 ++-- tests/v3/test_apps.py | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cloudfoundry_client/v3/entities.py b/cloudfoundry_client/v3/entities.py index de4b8b4..64f4d64 100644 --- a/cloudfoundry_client/v3/entities.py +++ b/cloudfoundry_client/v3/entities.py @@ -1,6 +1,6 @@ import functools from json import JSONDecodeError -from typing import Any, List, Tuple, Union, TypeVar, TYPE_CHECKING, Callable, Type +from typing import Any, List, Tuple, TypeVar, TYPE_CHECKING, Callable, Type from urllib.parse import quote, urlparse from requests import Response @@ -171,7 +171,7 @@ def _list(self, requested_path: str, entity_type: ENTITY_TYPE | None = None, **k return self._pagination(response_json, entity_type) def _attempt_to_paginate(self, url_requested: str, entity_type: ENTITY_TYPE | None = None) \ - -> Union[Pagination[Entity], Entity]: + -> Pagination[Entity] | Entity: response_json = self._read_response(self.client.get(url_requested), JsonObject) if "resources" in response_json: return self._pagination(response_json, entity_type) @@ -246,7 +246,7 @@ def get(self, entity_id: str, *extra_paths, **kwargs) -> Entity: requested_path = "%s%s/%s/%s" % (self.target_endpoint, self.entity_uri, entity_id, "/".join(extra_paths)) return self._get(requested_path, **kwargs) - def _read_response(self, response: Response, entity_type: ENTITY_TYPE | None) -> Union[JsonObject, Entity]: + def _read_response(self, response: Response, entity_type: ENTITY_TYPE | None) -> JsonObject | Entity: try: result = response.json(object_pairs_hook=JsonObject) except JSONDecodeError: @@ -288,7 +288,7 @@ def _include_resources(self, resource: JsonObject, result: JsonObject) -> None: def _get_entity_type(entity_name: str) -> Type[ENTITY_TYPE]: return Entity - def _entity(self, result: JsonObject, entity_type: ENTITY_TYPE | None) -> Union[JsonObject, Entity]: + def _entity(self, result: JsonObject, entity_type: ENTITY_TYPE | None) -> JsonObject | Entity: if "guid" in result or ("links" in result and "job" in result["links"]): return (entity_type or self.entity_type)(self.target_endpoint, self.client, **result) else: diff --git a/cloudfoundry_client/v3/service_credential_bindings.py b/cloudfoundry_client/v3/service_credential_bindings.py index b380b90..ff41c00 100644 --- a/cloudfoundry_client/v3/service_credential_bindings.py +++ b/cloudfoundry_client/v3/service_credential_bindings.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Union +from typing import TYPE_CHECKING from cloudfoundry_client.v3.entities import EntityManager, Entity, ToOneRelationship @@ -21,7 +21,7 @@ def create( meta_labels: dict | None, meta_annotations: dict | None, asynchronous: bool = True, - ) -> Union[str, Entity, None]: + ) -> str | Entity | None: data = { "name": name, "type": service_credential_binding_type, diff --git a/tests/v3/test_apps.py b/tests/v3/test_apps.py index 3b51108..2ce85d2 100644 --- a/tests/v3/test_apps.py +++ b/tests/v3/test_apps.py @@ -1,7 +1,7 @@ import unittest import yaml from http import HTTPStatus -from typing import List, Union +from typing import List from abstract_test_case import AbstractTestCase from cloudfoundry_client.common_objects import JsonObject, Pagination @@ -153,7 +153,7 @@ def test_get_manifest(self): self.assertIsInstance(application_services, list) self.assertEqual(len(application_services), 1) self.assertEqual(application_services[0], "my-service") - application_routes: List[Union[dict, str]] | None = application.get("routes") + application_routes: List[dict | str] | None = application.get("routes") self.assertIsInstance(application_routes, list) self.assertEqual(len(application_routes), 1) application_route: dict = application_routes[0] From 257fe6658d53953f21eb273dac032986f8e2d2a5 Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Tue, 6 Jan 2026 19:24:44 +0100 Subject: [PATCH 231/264] chore(code) remove List (#274) --- cloudfoundry_client/common_objects.py | 4 ++-- cloudfoundry_client/main/command_domain.py | 4 ++-- cloudfoundry_client/networking/entities.py | 6 +++--- cloudfoundry_client/networking/v1/external/policies.py | 5 ++--- cloudfoundry_client/operations/push/cf_ignore.py | 3 +-- cloudfoundry_client/operations/push/file_helper.py | 4 ++-- cloudfoundry_client/operations/push/push.py | 10 +++++----- cloudfoundry_client/v2/entities.py | 6 +++--- cloudfoundry_client/v2/resources.py | 4 ++-- cloudfoundry_client/v2/service_instances.py | 6 +++--- cloudfoundry_client/v3/entities.py | 4 ++-- cloudfoundry_client/v3/security_groups.py | 8 ++++---- cloudfoundry_client/v3/service_instances.py | 6 +++--- cloudfoundry_client/v3/service_plans.py | 6 +++--- tests/v3/test_apps.py | 3 +-- 15 files changed, 38 insertions(+), 41 deletions(-) diff --git a/cloudfoundry_client/common_objects.py b/cloudfoundry_client/common_objects.py index 99e9c49..f967c7b 100644 --- a/cloudfoundry_client/common_objects.py +++ b/cloudfoundry_client/common_objects.py @@ -1,5 +1,5 @@ import json -from typing import Callable, TypeVar, Generic, List +from typing import Callable, TypeVar, Generic class Request(dict): @@ -22,7 +22,7 @@ class Pagination(Generic[ENTITY]): def __init__(self, first_page: JsonObject, total_result: int, next_page_loader: Callable[[JsonObject], JsonObject | None], - resources_accessor: Callable[[JsonObject], List[JsonObject]], + resources_accessor: Callable[[JsonObject], list[JsonObject]], instance_creator: Callable[[JsonObject], ENTITY]): self._first_page = first_page self._total_results = total_result diff --git a/cloudfoundry_client/main/command_domain.py b/cloudfoundry_client/main/command_domain.py index 334b019..06d9e46 100644 --- a/cloudfoundry_client/main/command_domain.py +++ b/cloudfoundry_client/main/command_domain.py @@ -5,7 +5,7 @@ from argparse import _SubParsersAction, Namespace from collections import OrderedDict from http import HTTPStatus -from typing import Callable, Any, List +from typing import Callable, Any from cloudfoundry_client.client import CloudFoundryClient from cloudfoundry_client.errors import InvalidStatusCode @@ -60,7 +60,7 @@ def __init__( self.commands[command[0].entry] = command[0] self.extra_description[command[0].entry] = command[1] - def description(self) -> List[str]: + def description(self) -> list[str]: description = [ " %s" % self.display_name, " %s : List %ss" % (self._list_entry(), self.entity_name), diff --git a/cloudfoundry_client/networking/entities.py b/cloudfoundry_client/networking/entities.py index 02fbe09..73a453b 100644 --- a/cloudfoundry_client/networking/entities.py +++ b/cloudfoundry_client/networking/entities.py @@ -1,6 +1,6 @@ import logging from functools import reduce -from typing import Callable, List, Tuple, Any, Generator, TYPE_CHECKING +from typing import Callable, Tuple, Any, Generator, TYPE_CHECKING from urllib.parse import quote from requests import Response @@ -31,7 +31,7 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient", *args, ** raise InvalidEntity(**self) -EntityBuilder = Callable[[List[Tuple[str, Any]]], Entity] +EntityBuilder = Callable[[list[Tuple[str, Any]]], Entity] class EntityManager(object): @@ -105,7 +105,7 @@ def _get_entity_builder(self, entity_builder: EntityBuilder | None) -> EntityBui return entity_builder def _get_url_filtered(self, url: str, **kwargs) -> str: - def _append_encoded_parameter(parameters: List[str], args: Tuple[str, Any]) -> List[str]: + def _append_encoded_parameter(parameters: list[str], args: Tuple[str, Any]) -> list[str]: parameter_name, parameter_value = args[0], args[1] if parameter_name in self.list_query_parameters: parameters.append("%s=%s" % (parameter_name, str(parameter_value))) diff --git a/cloudfoundry_client/networking/v1/external/policies.py b/cloudfoundry_client/networking/v1/external/policies.py index 3b7c094..826093f 100644 --- a/cloudfoundry_client/networking/v1/external/policies.py +++ b/cloudfoundry_client/networking/v1/external/policies.py @@ -1,6 +1,5 @@ import logging from cloudfoundry_client.networking.entities import EntityManager -from typing import List _logger = logging.getLogger(__name__) @@ -45,7 +44,7 @@ class PolicyManager(EntityManager): def __init__(self, target_endpoint, client): super(PolicyManager, self).__init__(target_endpoint, client, "/networking/v1/external/policies") - def create(self, policies: List[Policy]): + def create(self, policies: list[Policy]): """create a new network policy Responses: @@ -62,7 +61,7 @@ def create(self, policies: List[Policy]): data.append(policy.dump()) return super(PolicyManager, self)._create({"policies": data}) - def delete(self, policies: List[Policy]): + def delete(self, policies: list[Policy]): """remove a new network policy Responses: diff --git a/cloudfoundry_client/operations/push/cf_ignore.py b/cloudfoundry_client/operations/push/cf_ignore.py index e04033f..7436582 100644 --- a/cloudfoundry_client/operations/push/cf_ignore.py +++ b/cloudfoundry_client/operations/push/cf_ignore.py @@ -1,7 +1,6 @@ import fnmatch import logging import os -from typing import List _logger = logging.getLogger(__name__) @@ -26,7 +25,7 @@ def is_relative_file_ignored(cf_ignore_entry): return any([is_relative_file_ignored(ignore_item) for ignore_item in self.ignore_items]) @staticmethod - def _pattern(pattern: str) -> List[str]: + def _pattern(pattern: str) -> list[str]: if pattern.find("/") < 0: return [pattern, os.path.join("**", pattern)] elif pattern.endswith("/"): diff --git a/cloudfoundry_client/operations/push/file_helper.py b/cloudfoundry_client/operations/push/file_helper.py index d4ac4fd..9f0afaa 100644 --- a/cloudfoundry_client/operations/push/file_helper.py +++ b/cloudfoundry_client/operations/push/file_helper.py @@ -2,7 +2,7 @@ import os import stat import zipfile -from typing import Callable, Generator, Tuple, List +from typing import Callable, Generator, Tuple class FileHelper(object): @@ -32,7 +32,7 @@ def unzip(path: str, tmp_dir: str): zip_ref.extract(entry, tmp_dir) @staticmethod - def walk(path: str) -> Generator[Tuple[str, List[str]], None, None]: + def walk(path: str) -> Generator[Tuple[str, list[str]], None, None]: for dir_path, _, files in os.walk(path, topdown=True): yield dir_path[len(path) :].lstrip("/"), files diff --git a/cloudfoundry_client/operations/push/push.py b/cloudfoundry_client/operations/push/push.py index b7f0f3b..fd95ef0 100644 --- a/cloudfoundry_client/operations/push/push.py +++ b/cloudfoundry_client/operations/push/push.py @@ -5,7 +5,7 @@ import shutil import tempfile import time -from typing import Tuple, List, Dict +from typing import Tuple, Dict from cloudfoundry_client.client import CloudFoundryClient from cloudfoundry_client.operations.push.cf_ignore import CfIgnore @@ -105,7 +105,7 @@ def _merge_environment(app: Entity | None, app_manifest: dict) -> dict: return environment def _route_application( - self, organization: Entity, space: Entity, app: Entity, no_route: bool, routes: List[str], random_route: bool + self, organization: Entity, space: Entity, app: Entity, no_route: bool, routes: list[str], random_route: bool ): existing_routes = [route for route in app.routes()] if no_route: @@ -115,7 +115,7 @@ def _route_application( else: self._build_new_requested_routes(organization, space, app, existing_routes, routes) - def _remove_all_routes(self, app: Entity, routes: List[Entity]): + def _remove_all_routes(self, app: Entity, routes: list[Entity]): for route in routes: self.client.v2.apps.remove_route(app["metadata"]["guid"], route["metadata"]["guid"]) @@ -142,7 +142,7 @@ def _build_default_route(self, space: Entity, app: Entity, random_route: bool): self.client.v2.apps.associate_route(app["metadata"]["guid"], route["metadata"]["guid"]) def _build_new_requested_routes( - self, organization: Entity, space: Entity, app: Entity, existing_routes: List[Entity], requested_routes: List[str] + self, organization: Entity, space: Entity, app: Entity, existing_routes: list[Entity], requested_routes: list[str] ): private_domains = {domain["entity"]["name"]: domain for domain in organization.private_domains()} shared_domains = {domain["entity"]["name"]: domain for domain in self.client.v2.shared_domains.list()} @@ -310,7 +310,7 @@ def _load_all_resources(top_directory: str) -> dict: ) return application_items - def _bind_services(self, space: Entity, app: Entity, services: List[str]): + def _bind_services(self, space: Entity, app: Entity, services: list[str]): service_instances = [ service_instance for service_instance in space.service_instances(return_user_provided_service_instances="true") ] diff --git a/cloudfoundry_client/v2/entities.py b/cloudfoundry_client/v2/entities.py index 0fac7bf..5196dd2 100644 --- a/cloudfoundry_client/v2/entities.py +++ b/cloudfoundry_client/v2/entities.py @@ -1,5 +1,5 @@ from functools import partial, reduce -from typing import Callable, List, Tuple, Any, TYPE_CHECKING +from typing import Callable, Tuple, Any, TYPE_CHECKING from urllib.parse import quote from requests import Response @@ -39,7 +39,7 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient", *args, ** raise InvalidEntity(**self) -EntityBuilder = Callable[[List[Tuple[str, Any]]], Entity] +EntityBuilder = Callable[[list[Tuple[str, Any]]], Entity] class EntityManager(object): @@ -141,7 +141,7 @@ def _get_entity_builder(self, entity_builder: EntityBuilder | None) -> EntityBui return entity_builder def _get_url_filtered(self, url: str, **kwargs) -> str: - def _append_encoded_parameter(parameters: List[str], args: Tuple[str, Any]) -> List[str]: + def _append_encoded_parameter(parameters: list[str], args: Tuple[str, Any]) -> list[str]: parameter_name, parameter_value = args[0], args[1] if parameter_name in self.list_query_parameters: parameters.append("%s=%s" % (parameter_name, str(parameter_value))) diff --git a/cloudfoundry_client/v2/resources.py b/cloudfoundry_client/v2/resources.py index d962daa..a7fee10 100644 --- a/cloudfoundry_client/v2/resources.py +++ b/cloudfoundry_client/v2/resources.py @@ -1,4 +1,4 @@ -from typing import List, TYPE_CHECKING +from typing import TYPE_CHECKING from cloudfoundry_client.common_objects import JsonObject @@ -11,6 +11,6 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): self.target_endpoint = target_endpoint self.client = client - def match(self, items: List[dict]) -> List[JsonObject]: + def match(self, items: list[dict]) -> list[JsonObject]: response = self.client.put("%s/v2/resource_match" % self.client.info.api_endpoint, json=items) return response.json(object_pairs_hook=JsonObject) diff --git a/cloudfoundry_client/v2/service_instances.py b/cloudfoundry_client/v2/service_instances.py index 8d6ebbd..2ac59db 100644 --- a/cloudfoundry_client/v2/service_instances.py +++ b/cloudfoundry_client/v2/service_instances.py @@ -1,4 +1,4 @@ -from typing import List, Dict, TYPE_CHECKING +from typing import Dict, TYPE_CHECKING from cloudfoundry_client.v2.entities import EntityManager, Entity @@ -18,7 +18,7 @@ def create( instance_name: str, plan_guid: str, parameters: dict | None = None, - tags: List[str] = None, + tags: list[str] = None, accepts_incomplete: bool | None = False, ) -> Entity: request = self._request(name=instance_name, space_guid=space_guid, service_plan_guid=plan_guid) @@ -33,7 +33,7 @@ def update( instance_name: str | None = None, plan_guid: str | None = None, parameters: dict | None = None, - tags: List[str] = None, + tags: list[str] = None, accepts_incomplete: bool | None = False, ) -> Entity: request = self._request() diff --git a/cloudfoundry_client/v3/entities.py b/cloudfoundry_client/v3/entities.py index 64f4d64..6c4c55e 100644 --- a/cloudfoundry_client/v3/entities.py +++ b/cloudfoundry_client/v3/entities.py @@ -1,6 +1,6 @@ import functools from json import JSONDecodeError -from typing import Any, List, Tuple, TypeVar, TYPE_CHECKING, Callable, Type +from typing import Any, Tuple, TypeVar, TYPE_CHECKING, Callable, Type from urllib.parse import quote, urlparse from requests import Response @@ -296,7 +296,7 @@ def _entity(self, result: JsonObject, entity_type: ENTITY_TYPE | None) -> JsonOb @staticmethod def _get_url_with_encoded_params(url: str, **kwargs) -> str: - def _append_encoded_parameter(parameters: List[str], args: Tuple[str, Any]) -> List[str]: + def _append_encoded_parameter(parameters: list[str], args: Tuple[str, Any]) -> list[str]: parameter_name, parameter_value = args[0], args[1] if isinstance(parameter_value, (list, tuple)): parameters.append("%s=%s" % (parameter_name, quote(",".join(parameter_value)))) diff --git a/cloudfoundry_client/v3/security_groups.py b/cloudfoundry_client/v3/security_groups.py index 43c32bc..dcbc459 100644 --- a/cloudfoundry_client/v3/security_groups.py +++ b/cloudfoundry_client/v3/security_groups.py @@ -1,6 +1,6 @@ from dataclasses import dataclass, asdict from enum import Enum, auto -from typing import TYPE_CHECKING, List +from typing import TYPE_CHECKING from cloudfoundry_client.v3.entities import EntityManager, ToManyRelationship, Entity, ToOneRelationship @@ -41,7 +41,7 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): def create(self, name: str, - rules: List[Rule] | None = None, + rules: list[Rule] | None = None, globally_enabled: GloballyEnabled | None = None, staging_spaces: ToManyRelationship | None = None, running_spaces: ToManyRelationship | None = None) -> Entity: @@ -51,7 +51,7 @@ def create(self, def update(self, security_group_id: str, name: str | None = None, - rules: List[Rule] | None = None, + rules: list[Rule] | None = None, globally_enabled: GloballyEnabled | None = None, staging_spaces: ToManyRelationship | None = None, running_spaces: ToManyRelationship | None = None) -> Entity: @@ -91,7 +91,7 @@ def _unbind_space(self, security_group_id: str, space_guid: ToOneRelationship, r @staticmethod def _generate_payload(name: str | None, - rules: List[Rule] | None, + rules: list[Rule] | None, globally_enabled: GloballyEnabled | None, staging_spaces: ToManyRelationship | None, running_spaces: ToManyRelationship | None): diff --git a/cloudfoundry_client/v3/service_instances.py b/cloudfoundry_client/v3/service_instances.py index cfa253f..d882604 100644 --- a/cloudfoundry_client/v3/service_instances.py +++ b/cloudfoundry_client/v3/service_instances.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, List +from typing import TYPE_CHECKING from cloudfoundry_client.common_objects import JsonObject from cloudfoundry_client.v3.entities import Entity, EntityManager, ToOneRelationship @@ -19,7 +19,7 @@ def create( meta_labels: dict | None = None, meta_annotations: dict | None = None, parameters: dict | None = None, - tags: List[str] | None = None, + tags: list[str] | None = None, ) -> Entity: data = { "name": name, @@ -48,7 +48,7 @@ def update( maintenance_info: str | None = None, meta_labels: dict | None = None, meta_annotations: dict | None = None, - tags: List[str] | None = None + tags: list[str] | None = None ) -> Entity: data = {} if name: diff --git a/cloudfoundry_client/v3/service_plans.py b/cloudfoundry_client/v3/service_plans.py index 0534cec..ab28ca5 100644 --- a/cloudfoundry_client/v3/service_plans.py +++ b/cloudfoundry_client/v3/service_plans.py @@ -1,4 +1,4 @@ -from typing import Dict, List, TYPE_CHECKING +from typing import Dict, TYPE_CHECKING from cloudfoundry_client.v3.entities import EntityManager, Entity @@ -32,7 +32,7 @@ def get_visibility(self, service_plan_guid: str) -> Dict: # Updates a service plan visibility. It behaves similar to the POST service plan visibility endpoint but # this endpoint will REPLACE the existing list of organizations when the service plan is organization visible. - def update_visibility(self, service_plan_guid: str, type: str, organizations: List[dict] | None = None) -> Dict: + def update_visibility(self, service_plan_guid: str, type: str, organizations: list[dict] | None = None) -> Dict: payload = {"type": type} if organizations: payload["organizations"] = organizations @@ -42,7 +42,7 @@ def update_visibility(self, service_plan_guid: str, type: str, organizations: Li # Applies a service plan visibility. It behaves similar to the PATCH service plan visibility endpoint but # this endpoint will APPEND to the existing list of organizations when the service plan is organization visible. - def apply_visibility_to_extra_orgs(self, service_plan_guid: str, organizations: List[dict]) -> Dict: + def apply_visibility_to_extra_orgs(self, service_plan_guid: str, organizations: list[dict]) -> Dict: payload = {"type": "organization", "organizations": organizations} return super(ServicePlanManager, self)._post( url=f"{self.target_endpoint}{self.entity_uri}/{service_plan_guid}/visibility", data=payload, files=None diff --git a/tests/v3/test_apps.py b/tests/v3/test_apps.py index 2ce85d2..6039f1b 100644 --- a/tests/v3/test_apps.py +++ b/tests/v3/test_apps.py @@ -1,7 +1,6 @@ import unittest import yaml from http import HTTPStatus -from typing import List from abstract_test_case import AbstractTestCase from cloudfoundry_client.common_objects import JsonObject, Pagination @@ -153,7 +152,7 @@ def test_get_manifest(self): self.assertIsInstance(application_services, list) self.assertEqual(len(application_services), 1) self.assertEqual(application_services[0], "my-service") - application_routes: List[dict | str] | None = application.get("routes") + application_routes: list[dict | str] | None = application.get("routes") self.assertIsInstance(application_routes, list) self.assertEqual(len(application_routes), 1) application_route: dict = application_routes[0] From 62ca995551672422cc090baa94cee2b66e5a45d1 Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Tue, 6 Jan 2026 19:32:01 +0100 Subject: [PATCH 232/264] chore(code) remove Dict (#275) --- cloudfoundry_client/operations/push/push.py | 6 +++--- cloudfoundry_client/v2/apps.py | 16 ++++++++-------- cloudfoundry_client/v2/service_instances.py | 4 ++-- cloudfoundry_client/v3/service_plans.py | 8 ++++---- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/cloudfoundry_client/operations/push/push.py b/cloudfoundry_client/operations/push/push.py index fd95ef0..53388ae 100644 --- a/cloudfoundry_client/operations/push/push.py +++ b/cloudfoundry_client/operations/push/push.py @@ -5,7 +5,7 @@ import shutil import tempfile import time -from typing import Tuple, Dict +from typing import Tuple from cloudfoundry_client.client import CloudFoundryClient from cloudfoundry_client.operations.push.cf_ignore import CfIgnore @@ -211,7 +211,7 @@ def _resolve_new_tcp_route(self, space: Entity, domain: Entity, port: int) -> En return existing_route @staticmethod - def _split_route(requested_route: Dict[str, str]) -> Tuple[str, int, str]: + def _split_route(requested_route: dict[str, str]) -> Tuple[str, int, str]: route_splitted = PushOperation.SPLIT_ROUTE_PATTERN.match(requested_route["route"]) if route_splitted is None: raise AssertionError("Invalid route: %s" % requested_route["route"]) @@ -222,7 +222,7 @@ def _split_route(requested_route: Dict[str, str]) -> Tuple[str, int, str]: @staticmethod def _resolve_domain( - route: str, private_domains: Dict[str, Entity], shared_domains: Dict[str, Entity] + route: str, private_domains: dict[str, Entity], shared_domains: dict[str, Entity] ) -> Tuple[str, str, Entity]: for domains in [private_domains, shared_domains]: if route in domains: diff --git a/cloudfoundry_client/v2/apps.py b/cloudfoundry_client/v2/apps.py index 857a76f..ebb8379 100644 --- a/cloudfoundry_client/v2/apps.py +++ b/cloudfoundry_client/v2/apps.py @@ -3,7 +3,7 @@ import os from http import HTTPStatus from time import sleep -from typing import Dict, TYPE_CHECKING +from typing import TYPE_CHECKING from cloudfoundry_client.doppler.client import EnvelopeStream from cloudfoundry_client.errors import InvalidStatusCode @@ -17,7 +17,7 @@ class Application(Entity): - def instances(self) -> Dict[str, JsonObject]: + def instances(self) -> dict[str, JsonObject]: return self.client.v2.apps.get_instances(self["metadata"]["guid"]) def start(self) -> "Application": @@ -29,10 +29,10 @@ def stop(self) -> "Application": def restart_instance(self, instance_id: int): return self.client.v2.apps.restart_instance(self["metadata"]["guid"], instance_id) - def stats(self) -> Dict[str, JsonObject]: + def stats(self) -> dict[str, JsonObject]: return self.client.v2.apps.get_stats(self["metadata"]["guid"]) - def env(self) -> Dict[str, JsonObject]: + def env(self) -> dict[str, JsonObject]: return self.client.v2.apps.get_env(self["metadata"]["guid"]) def summary(self) -> JsonObject: @@ -80,13 +80,13 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): target_endpoint, client, "/v2/apps", lambda pairs: Application(target_endpoint, client, pairs) ) - def get_stats(self, application_guid: str) -> Dict[str, JsonObject]: + def get_stats(self, application_guid: str) -> dict[str, JsonObject]: return self._get("%s/%s/stats" % (self.entity_uri, application_guid), JsonObject) - def get_instances(self, application_guid: str) -> Dict[str, JsonObject]: + def get_instances(self, application_guid: str) -> dict[str, JsonObject]: return self._get("%s/%s/instances" % (self.entity_uri, application_guid), JsonObject) - def get_env(self, application_guid: str) -> Dict[str, JsonObject]: + def get_env(self, application_guid: str) -> dict[str, JsonObject]: return self._get("%s/%s/env" % (self.entity_uri, application_guid), JsonObject) def get_summary(self, application_guid: str) -> JsonObject: @@ -195,7 +195,7 @@ def _wait_for_instances_in_state( sleep(check_time) sum_waiting += check_time - def _safe_get_instances(self, application_guid: str) -> Dict[str, JsonObject]: + def _safe_get_instances(self, application_guid: str) -> dict[str, JsonObject]: try: return self.get_instances(application_guid) except InvalidStatusCode as ex: diff --git a/cloudfoundry_client/v2/service_instances.py b/cloudfoundry_client/v2/service_instances.py index 2ac59db..8213572 100644 --- a/cloudfoundry_client/v2/service_instances.py +++ b/cloudfoundry_client/v2/service_instances.py @@ -1,4 +1,4 @@ -from typing import Dict, TYPE_CHECKING +from typing import TYPE_CHECKING from cloudfoundry_client.v2.entities import EntityManager, Entity @@ -44,7 +44,7 @@ def update( params = None if not accepts_incomplete else dict(accepts_incomplete="true") return super(ServiceInstanceManager, self)._update(instance_guid, request, params=params) - def list_permissions(self, instance_guid: str) -> Dict[str, bool]: + def list_permissions(self, instance_guid: str) -> dict[str, bool]: return super(ServiceInstanceManager, self)._get("%s/%s/permissions" % (self.entity_uri, instance_guid), dict) def remove(self, instance_guid: str, accepts_incomplete: bool | None = False, purge: bool | None = False): diff --git a/cloudfoundry_client/v3/service_plans.py b/cloudfoundry_client/v3/service_plans.py index ab28ca5..f2333aa 100644 --- a/cloudfoundry_client/v3/service_plans.py +++ b/cloudfoundry_client/v3/service_plans.py @@ -1,4 +1,4 @@ -from typing import Dict, TYPE_CHECKING +from typing import TYPE_CHECKING from cloudfoundry_client.v3.entities import EntityManager, Entity @@ -27,12 +27,12 @@ def update( def remove(self, guid: str): super(ServicePlanManager, self)._remove(guid) - def get_visibility(self, service_plan_guid: str) -> Dict: + def get_visibility(self, service_plan_guid: str) -> dict: return super(ServicePlanManager, self)._get(f"{self.target_endpoint}{self.entity_uri}/{service_plan_guid}/visibility") # Updates a service plan visibility. It behaves similar to the POST service plan visibility endpoint but # this endpoint will REPLACE the existing list of organizations when the service plan is organization visible. - def update_visibility(self, service_plan_guid: str, type: str, organizations: list[dict] | None = None) -> Dict: + def update_visibility(self, service_plan_guid: str, type: str, organizations: list[dict] | None = None) -> dict: payload = {"type": type} if organizations: payload["organizations"] = organizations @@ -42,7 +42,7 @@ def update_visibility(self, service_plan_guid: str, type: str, organizations: li # Applies a service plan visibility. It behaves similar to the PATCH service plan visibility endpoint but # this endpoint will APPEND to the existing list of organizations when the service plan is organization visible. - def apply_visibility_to_extra_orgs(self, service_plan_guid: str, organizations: list[dict]) -> Dict: + def apply_visibility_to_extra_orgs(self, service_plan_guid: str, organizations: list[dict]) -> dict: payload = {"type": "organization", "organizations": organizations} return super(ServicePlanManager, self)._post( url=f"{self.target_endpoint}{self.entity_uri}/{service_plan_guid}/visibility", data=payload, files=None From fb564134bf6ee503649ea5b5b17c3ed96311d148 Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Tue, 6 Jan 2026 19:46:26 +0100 Subject: [PATCH 233/264] chore(code) remove Tuple/Callable (#276) --- cloudfoundry_client/common_objects.py | 3 ++- cloudfoundry_client/doppler/websocket_envelope_reader.py | 4 ++-- cloudfoundry_client/main/apps_command_domain.py | 2 +- cloudfoundry_client/main/command_domain.py | 3 ++- cloudfoundry_client/main/main.py | 5 +++-- cloudfoundry_client/main/operation_commands.py | 3 +-- cloudfoundry_client/networking/entities.py | 7 ++++--- cloudfoundry_client/operations/push/file_helper.py | 5 +++-- cloudfoundry_client/operations/push/push.py | 7 +++---- cloudfoundry_client/v2/entities.py | 7 ++++--- cloudfoundry_client/v3/entities.py | 5 +++-- 11 files changed, 28 insertions(+), 23 deletions(-) diff --git a/cloudfoundry_client/common_objects.py b/cloudfoundry_client/common_objects.py index f967c7b..71fe08b 100644 --- a/cloudfoundry_client/common_objects.py +++ b/cloudfoundry_client/common_objects.py @@ -1,5 +1,6 @@ import json -from typing import Callable, TypeVar, Generic +from collections.abc import Callable +from typing import TypeVar, Generic class Request(dict): diff --git a/cloudfoundry_client/doppler/websocket_envelope_reader.py b/cloudfoundry_client/doppler/websocket_envelope_reader.py index 1a2d77d..d4e63b3 100644 --- a/cloudfoundry_client/doppler/websocket_envelope_reader.py +++ b/cloudfoundry_client/doppler/websocket_envelope_reader.py @@ -1,5 +1,5 @@ import ssl -from typing import Callable, Tuple +from collections.abc import Callable import websocket @@ -12,7 +12,7 @@ def __init__( verify_ssl: bool = True, proxy_host: str | None = None, proxy_port: int | None = None, - proxy_auth: Tuple[str, str] | None = None, + proxy_auth: tuple[str, str] | None = None, ): if not verify_ssl: self._ws = websocket.WebSocket(sslopt=dict(cert_reqs=ssl.CERT_NONE)) diff --git a/cloudfoundry_client/main/apps_command_domain.py b/cloudfoundry_client/main/apps_command_domain.py index 3d946b8..206ae2e 100644 --- a/cloudfoundry_client/main/apps_command_domain.py +++ b/cloudfoundry_client/main/apps_command_domain.py @@ -1,5 +1,5 @@ from argparse import _SubParsersAction, Namespace -from typing import Callable +from collections.abc import Callable from cloudfoundry_client.client import CloudFoundryClient from cloudfoundry_client.main.command_domain import CommandDomain, Command diff --git a/cloudfoundry_client/main/command_domain.py b/cloudfoundry_client/main/command_domain.py index 06d9e46..0910975 100644 --- a/cloudfoundry_client/main/command_domain.py +++ b/cloudfoundry_client/main/command_domain.py @@ -4,8 +4,9 @@ import re from argparse import _SubParsersAction, Namespace from collections import OrderedDict +from collections.abc import Callable from http import HTTPStatus -from typing import Callable, Any +from typing import Any from cloudfoundry_client.client import CloudFoundryClient from cloudfoundry_client.errors import InvalidStatusCode diff --git a/cloudfoundry_client/main/main.py b/cloudfoundry_client/main/main.py index e8027b0..6d5b4b7 100644 --- a/cloudfoundry_client/main/main.py +++ b/cloudfoundry_client/main/main.py @@ -5,8 +5,9 @@ import os import re import sys +from collections.abc import Callable from http import HTTPStatus -from typing import Tuple, Callable, Any +from typing import Any from requests.exceptions import ConnectionError @@ -152,7 +153,7 @@ def _get_v2_client_domain(client: CloudFoundryClient, domain: str) -> Any: return getattr(client.v2, "%ss" % domain) -def generate_oauth_token_command() -> Tuple[Command, str]: +def generate_oauth_token_command() -> tuple[Command, str]: entry = "oauth-token" def generate_parser(parser: argparse._SubParsersAction): diff --git a/cloudfoundry_client/main/operation_commands.py b/cloudfoundry_client/main/operation_commands.py index 8ef39e1..89db16a 100644 --- a/cloudfoundry_client/main/operation_commands.py +++ b/cloudfoundry_client/main/operation_commands.py @@ -1,12 +1,11 @@ from argparse import _SubParsersAction, Namespace -from typing import Tuple from cloudfoundry_client.client import CloudFoundryClient from cloudfoundry_client.main.command_domain import Command from cloudfoundry_client.operations.push.push import PushOperation -def generate_push_command() -> Tuple[Command, str]: +def generate_push_command() -> tuple[Command, str]: entry = "push_app" def generate_parser(parser: _SubParsersAction): diff --git a/cloudfoundry_client/networking/entities.py b/cloudfoundry_client/networking/entities.py index 73a453b..6211de5 100644 --- a/cloudfoundry_client/networking/entities.py +++ b/cloudfoundry_client/networking/entities.py @@ -1,6 +1,7 @@ import logging +from collections.abc import Callable from functools import reduce -from typing import Callable, Tuple, Any, Generator, TYPE_CHECKING +from typing import Any, Generator, TYPE_CHECKING from urllib.parse import quote from requests import Response @@ -31,7 +32,7 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient", *args, ** raise InvalidEntity(**self) -EntityBuilder = Callable[[list[Tuple[str, Any]]], Entity] +EntityBuilder = Callable[[list[tuple[str, Any]]], Entity] class EntityManager(object): @@ -105,7 +106,7 @@ def _get_entity_builder(self, entity_builder: EntityBuilder | None) -> EntityBui return entity_builder def _get_url_filtered(self, url: str, **kwargs) -> str: - def _append_encoded_parameter(parameters: list[str], args: Tuple[str, Any]) -> list[str]: + def _append_encoded_parameter(parameters: list[str], args: tuple[str, Any]) -> list[str]: parameter_name, parameter_value = args[0], args[1] if parameter_name in self.list_query_parameters: parameters.append("%s=%s" % (parameter_name, str(parameter_value))) diff --git a/cloudfoundry_client/operations/push/file_helper.py b/cloudfoundry_client/operations/push/file_helper.py index 9f0afaa..30acdcc 100644 --- a/cloudfoundry_client/operations/push/file_helper.py +++ b/cloudfoundry_client/operations/push/file_helper.py @@ -2,7 +2,8 @@ import os import stat import zipfile -from typing import Callable, Generator, Tuple +from collections.abc import Callable +from typing import Generator class FileHelper(object): @@ -32,7 +33,7 @@ def unzip(path: str, tmp_dir: str): zip_ref.extract(entry, tmp_dir) @staticmethod - def walk(path: str) -> Generator[Tuple[str, list[str]], None, None]: + def walk(path: str) -> Generator[tuple[str, list[str]], None, None]: for dir_path, _, files in os.walk(path, topdown=True): yield dir_path[len(path) :].lstrip("/"), files diff --git a/cloudfoundry_client/operations/push/push.py b/cloudfoundry_client/operations/push/push.py index 53388ae..75de4cc 100644 --- a/cloudfoundry_client/operations/push/push.py +++ b/cloudfoundry_client/operations/push/push.py @@ -5,7 +5,6 @@ import shutil import tempfile import time -from typing import Tuple from cloudfoundry_client.client import CloudFoundryClient from cloudfoundry_client.operations.push.cf_ignore import CfIgnore @@ -32,7 +31,7 @@ def push(self, space_id: str, manifest_path: str, restart: bool = True): if "path" in app_manifest or "docker" in app_manifest: self._push_application(organization, space, app_manifest, restart) - def _retrieve_space_and_organization(self, space_id: str) -> Tuple[Entity, Entity]: + def _retrieve_space_and_organization(self, space_id: str) -> tuple[Entity, Entity]: space = self.client.v2.spaces.get(space_id) organization = space.organization() return organization, space @@ -211,7 +210,7 @@ def _resolve_new_tcp_route(self, space: Entity, domain: Entity, port: int) -> En return existing_route @staticmethod - def _split_route(requested_route: dict[str, str]) -> Tuple[str, int, str]: + def _split_route(requested_route: dict[str, str]) -> tuple[str, int, str]: route_splitted = PushOperation.SPLIT_ROUTE_PATTERN.match(requested_route["route"]) if route_splitted is None: raise AssertionError("Invalid route: %s" % requested_route["route"]) @@ -223,7 +222,7 @@ def _split_route(requested_route: dict[str, str]) -> Tuple[str, int, str]: @staticmethod def _resolve_domain( route: str, private_domains: dict[str, Entity], shared_domains: dict[str, Entity] - ) -> Tuple[str, str, Entity]: + ) -> tuple[str, str, Entity]: for domains in [private_domains, shared_domains]: if route in domains: return "", route, domains[route] diff --git a/cloudfoundry_client/v2/entities.py b/cloudfoundry_client/v2/entities.py index 5196dd2..e71440e 100644 --- a/cloudfoundry_client/v2/entities.py +++ b/cloudfoundry_client/v2/entities.py @@ -1,5 +1,6 @@ +from collections.abc import Callable from functools import partial, reduce -from typing import Callable, Tuple, Any, TYPE_CHECKING +from typing import Any, TYPE_CHECKING from urllib.parse import quote from requests import Response @@ -39,7 +40,7 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient", *args, ** raise InvalidEntity(**self) -EntityBuilder = Callable[[list[Tuple[str, Any]]], Entity] +EntityBuilder = Callable[[list[tuple[str, Any]]], Entity] class EntityManager(object): @@ -141,7 +142,7 @@ def _get_entity_builder(self, entity_builder: EntityBuilder | None) -> EntityBui return entity_builder def _get_url_filtered(self, url: str, **kwargs) -> str: - def _append_encoded_parameter(parameters: list[str], args: Tuple[str, Any]) -> list[str]: + def _append_encoded_parameter(parameters: list[str], args: tuple[str, Any]) -> list[str]: parameter_name, parameter_value = args[0], args[1] if parameter_name in self.list_query_parameters: parameters.append("%s=%s" % (parameter_name, str(parameter_value))) diff --git a/cloudfoundry_client/v3/entities.py b/cloudfoundry_client/v3/entities.py index 6c4c55e..6ddacd8 100644 --- a/cloudfoundry_client/v3/entities.py +++ b/cloudfoundry_client/v3/entities.py @@ -1,6 +1,7 @@ import functools +from collections.abc import Callable from json import JSONDecodeError -from typing import Any, Tuple, TypeVar, TYPE_CHECKING, Callable, Type +from typing import Any, TypeVar, TYPE_CHECKING, Type from urllib.parse import quote, urlparse from requests import Response @@ -296,7 +297,7 @@ def _entity(self, result: JsonObject, entity_type: ENTITY_TYPE | None) -> JsonOb @staticmethod def _get_url_with_encoded_params(url: str, **kwargs) -> str: - def _append_encoded_parameter(parameters: list[str], args: Tuple[str, Any]) -> list[str]: + def _append_encoded_parameter(parameters: list[str], args: tuple[str, Any]) -> list[str]: parameter_name, parameter_value = args[0], args[1] if isinstance(parameter_value, (list, tuple)): parameters.append("%s=%s" % (parameter_name, quote(",".join(parameter_value)))) From ee267ac8a848c31f0a8236bfb463babf141fc1db Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Tue, 6 Jan 2026 20:28:54 +0100 Subject: [PATCH 234/264] chore(code) remove Generator (#277) --- cloudfoundry_client/common_objects.py | 21 ++- cloudfoundry_client/doppler/client.py | 2 +- cloudfoundry_client/networking/entities.py | 4 +- .../operations/push/file_helper.py | 3 +- cloudfoundry_client/v2/events.py | 3 +- .../{fake => operations}/manifest_main.yml | 0 ...valid_entity_with_invalid_entity_type.json | 0 .../GET_invalid_entity_with_null_entity.json | 0 .../GET_invalid_entity_without_entity.json | 0 .../fake/GET_multi_page_0_response.json | 0 .../fake/GET_multi_page_1_response.json | 0 .../fixtures/{ => v2}/fake/GET_response.json | 0 .../{ => v2}/fake/GET_{id}_response.json | 0 .../v3/fake/GET_multi_page_0_response.json | 144 ++++++++++++++++++ .../v3/fake/GET_multi_page_1_response.json | 144 ++++++++++++++++++ tests/operations/push/test_push.py | 10 +- tests/v2/test_entities.py | 36 +++-- tests/v3/test_entities.py | 34 +++++ 18 files changed, 371 insertions(+), 30 deletions(-) rename tests/fixtures/{fake => operations}/manifest_main.yml (100%) rename tests/fixtures/{ => v2}/fake/GET_invalid_entity_with_invalid_entity_type.json (100%) rename tests/fixtures/{ => v2}/fake/GET_invalid_entity_with_null_entity.json (100%) rename tests/fixtures/{ => v2}/fake/GET_invalid_entity_without_entity.json (100%) rename tests/fixtures/{ => v2}/fake/GET_multi_page_0_response.json (100%) rename tests/fixtures/{ => v2}/fake/GET_multi_page_1_response.json (100%) rename tests/fixtures/{ => v2}/fake/GET_response.json (100%) rename tests/fixtures/{ => v2}/fake/GET_{id}_response.json (100%) create mode 100644 tests/fixtures/v3/fake/GET_multi_page_0_response.json create mode 100644 tests/fixtures/v3/fake/GET_multi_page_1_response.json diff --git a/cloudfoundry_client/common_objects.py b/cloudfoundry_client/common_objects.py index 71fe08b..57d97e5 100644 --- a/cloudfoundry_client/common_objects.py +++ b/cloudfoundry_client/common_objects.py @@ -1,5 +1,5 @@ import json -from collections.abc import Callable +from collections.abc import Callable, Generator from typing import TypeVar, Generic @@ -19,7 +19,7 @@ def __init__(self, *args, **kwargs): ENTITY = TypeVar('ENTITY') -class Pagination(Generic[ENTITY]): +class Pagination(Generic[ENTITY], Generator[ENTITY, None, None]): def __init__(self, first_page: JsonObject, total_result: int, next_page_loader: Callable[[JsonObject], JsonObject | None], @@ -37,10 +37,7 @@ def __init__(self, first_page: JsonObject, def total_results(self) -> int: return self._total_results - def __iter__(self): - return self - - def __next__(self) -> ENTITY: + def send(self, value) -> ENTITY: try: if self._cursor is None: self._current_page = self._first_page @@ -52,3 +49,15 @@ def __next__(self) -> ENTITY: raise self._cursor = self._resources_accessor(self._current_page).__iter__() return self._instance_creator(self._cursor.__next__()) + + def throw(self, typ, val=None, tb=None): + super().throw(typ, val, tb) + + def close(self): + super().close() + + def __iter__(self): + return self + + def __next__(self) -> ENTITY: + return self.send(None) diff --git a/cloudfoundry_client/doppler/client.py b/cloudfoundry_client/doppler/client.py index 7f4e5ad..4081e59 100644 --- a/cloudfoundry_client/doppler/client.py +++ b/cloudfoundry_client/doppler/client.py @@ -1,6 +1,6 @@ import logging import re -from typing import Generator +from collections.abc import Generator from urllib.parse import urlparse from oauth2_client.credentials_manager import CredentialManager diff --git a/cloudfoundry_client/networking/entities.py b/cloudfoundry_client/networking/entities.py index 6211de5..e28c06f 100644 --- a/cloudfoundry_client/networking/entities.py +++ b/cloudfoundry_client/networking/entities.py @@ -1,7 +1,7 @@ import logging -from collections.abc import Callable +from collections.abc import Callable, Generator from functools import reduce -from typing import Any, Generator, TYPE_CHECKING +from typing import Any, TYPE_CHECKING from urllib.parse import quote from requests import Response diff --git a/cloudfoundry_client/operations/push/file_helper.py b/cloudfoundry_client/operations/push/file_helper.py index 30acdcc..94e9b18 100644 --- a/cloudfoundry_client/operations/push/file_helper.py +++ b/cloudfoundry_client/operations/push/file_helper.py @@ -2,8 +2,7 @@ import os import stat import zipfile -from collections.abc import Callable -from typing import Generator +from collections.abc import Callable, Generator class FileHelper(object): diff --git a/cloudfoundry_client/v2/events.py b/cloudfoundry_client/v2/events.py index 9822165..c71e802 100644 --- a/cloudfoundry_client/v2/events.py +++ b/cloudfoundry_client/v2/events.py @@ -1,4 +1,5 @@ -from typing import Generator, TYPE_CHECKING +from collections.abc import Generator +from typing import TYPE_CHECKING from cloudfoundry_client.v2.entities import EntityManager, Entity diff --git a/tests/fixtures/fake/manifest_main.yml b/tests/fixtures/operations/manifest_main.yml similarity index 100% rename from tests/fixtures/fake/manifest_main.yml rename to tests/fixtures/operations/manifest_main.yml diff --git a/tests/fixtures/fake/GET_invalid_entity_with_invalid_entity_type.json b/tests/fixtures/v2/fake/GET_invalid_entity_with_invalid_entity_type.json similarity index 100% rename from tests/fixtures/fake/GET_invalid_entity_with_invalid_entity_type.json rename to tests/fixtures/v2/fake/GET_invalid_entity_with_invalid_entity_type.json diff --git a/tests/fixtures/fake/GET_invalid_entity_with_null_entity.json b/tests/fixtures/v2/fake/GET_invalid_entity_with_null_entity.json similarity index 100% rename from tests/fixtures/fake/GET_invalid_entity_with_null_entity.json rename to tests/fixtures/v2/fake/GET_invalid_entity_with_null_entity.json diff --git a/tests/fixtures/fake/GET_invalid_entity_without_entity.json b/tests/fixtures/v2/fake/GET_invalid_entity_without_entity.json similarity index 100% rename from tests/fixtures/fake/GET_invalid_entity_without_entity.json rename to tests/fixtures/v2/fake/GET_invalid_entity_without_entity.json diff --git a/tests/fixtures/fake/GET_multi_page_0_response.json b/tests/fixtures/v2/fake/GET_multi_page_0_response.json similarity index 100% rename from tests/fixtures/fake/GET_multi_page_0_response.json rename to tests/fixtures/v2/fake/GET_multi_page_0_response.json diff --git a/tests/fixtures/fake/GET_multi_page_1_response.json b/tests/fixtures/v2/fake/GET_multi_page_1_response.json similarity index 100% rename from tests/fixtures/fake/GET_multi_page_1_response.json rename to tests/fixtures/v2/fake/GET_multi_page_1_response.json diff --git a/tests/fixtures/fake/GET_response.json b/tests/fixtures/v2/fake/GET_response.json similarity index 100% rename from tests/fixtures/fake/GET_response.json rename to tests/fixtures/v2/fake/GET_response.json diff --git a/tests/fixtures/fake/GET_{id}_response.json b/tests/fixtures/v2/fake/GET_{id}_response.json similarity index 100% rename from tests/fixtures/fake/GET_{id}_response.json rename to tests/fixtures/v2/fake/GET_{id}_response.json diff --git a/tests/fixtures/v3/fake/GET_multi_page_0_response.json b/tests/fixtures/v3/fake/GET_multi_page_0_response.json new file mode 100644 index 0000000..df09c93 --- /dev/null +++ b/tests/fixtures/v3/fake/GET_multi_page_0_response.json @@ -0,0 +1,144 @@ +{ + "pagination": { + "total_results": 3, + "total_pages": 2, + "first": { + "href": "http://somewhere.org/fake" + }, + "last": { + "href": "http://somewhere.org/fake/last?page=2&per_page=2" + }, + "next": { + "href": "http://somewhere.org/fake/last?page=2&per_page=2" + }, + "previous": null + }, + "resources": [ + { + "guid": "1cb006ee-fb05-47e1-b541-c34179ddc446", + "name": "my_app", + "state": "STARTED", + "created_at": "2016-03-17T21:41:30Z", + "updated_at": "2016-03-18T11:32:30Z", + "lifecycle": { + "type": "buildpack", + "data": { + "buildpacks": [ + "java_buildpack" + ], + "stack": "cflinuxfs2" + } + }, + "relationships": { + "space": { + "data": { + "guid": "2f35885d-0c9d-4423-83ad-fd05066f8576" + } + } + }, + "links": { + "self": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446" + }, + "space": { + "href": "http://somewhere.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576" + }, + "processes": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/processes" + }, + "route_mappings": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/route_mappings" + }, + "packages": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/packages" + }, + "environment_variables": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/environment_variables" + }, + "current_droplet": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/droplets/current" + }, + "droplets": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/droplets" + }, + "tasks": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/tasks" + }, + "start": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/actions/start", + "method": "POST" + }, + "stop": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/actions/stop", + "method": "POST" + } + }, + "metadata": { + "labels": {} + } + }, + { + "guid": "02b4ec9b-94c7-4468-9c23-4e906191a0f8", + "name": "my_app2", + "state": "STOPPED", + "created_at": "1970-01-01T00:00:02Z", + "updated_at": "2016-06-08T16:41:26Z", + "lifecycle": { + "type": "buildpack", + "data": { + "buildpacks": [ + "ruby_buildpack" + ], + "stack": "cflinuxfs2" + } + }, + "relationships": { + "space": { + "data": { + "guid": "2f35885d-0c9d-4423-83ad-fd05066f8576" + } + } + }, + "links": { + "self": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8" + }, + "space": { + "href": "http://somewhere.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576" + }, + "processes": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/processes" + }, + "route_mappings": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/route_mappings" + }, + "packages": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/packages" + }, + "environment_variables": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/environment_variables" + }, + "current_droplet": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/droplets/current" + }, + "droplets": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/droplets" + }, + "tasks": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/tasks" + }, + "start": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/actions/start", + "method": "POST" + }, + "stop": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/actions/stop", + "method": "POST" + } + }, + "metadata": { + "labels": {} + } + } + ] +} \ No newline at end of file diff --git a/tests/fixtures/v3/fake/GET_multi_page_1_response.json b/tests/fixtures/v3/fake/GET_multi_page_1_response.json new file mode 100644 index 0000000..d28ef45 --- /dev/null +++ b/tests/fixtures/v3/fake/GET_multi_page_1_response.json @@ -0,0 +1,144 @@ +{ + "pagination": { + "total_results": 3, + "total_pages": 2, + "first": { + "href": "http://somewhere.org/fake" + }, + "last": { + "href": "http://somewhere.org/fake/last?page=2&per_page=2" + }, + "next": null, + "previous": { + "href": "http://somewhere.org/fake" + } + }, + "resources": [ + { + "guid": "1cb006ee-fb05-47e1-b541-c34179ddc447", + "name": "my_app3", + "state": "STARTED", + "created_at": "2016-03-17T21:41:30Z", + "updated_at": "2016-03-18T11:32:30Z", + "lifecycle": { + "type": "buildpack", + "data": { + "buildpacks": [ + "java_buildpack" + ], + "stack": "cflinuxfs2" + } + }, + "relationships": { + "space": { + "data": { + "guid": "2f35885d-0c9d-4423-83ad-fd05066f8576" + } + } + }, + "links": { + "self": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc447" + }, + "space": { + "href": "http://somewhere.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576" + }, + "processes": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc447/processes" + }, + "route_mappings": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc447/route_mappings" + }, + "packages": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc447/packages" + }, + "environment_variables": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc447/environment_variables" + }, + "current_droplet": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc447/droplets/current" + }, + "droplets": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc447/droplets" + }, + "tasks": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc447/tasks" + }, + "start": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc447/actions/start", + "method": "POST" + }, + "stop": { + "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc447/actions/stop", + "method": "POST" + } + }, + "metadata": { + "labels": {} + } + }, + { + "guid": "02b4ec9b-94c7-4468-9c23-4e906191a0f9", + "name": "my_app4", + "state": "STOPPED", + "created_at": "1970-01-01T00:00:02Z", + "updated_at": "2016-06-08T16:41:26Z", + "lifecycle": { + "type": "buildpack", + "data": { + "buildpacks": [ + "ruby_buildpack" + ], + "stack": "cflinuxfs2" + } + }, + "relationships": { + "space": { + "data": { + "guid": "2f35885d-0c9d-4423-83ad-fd05066f8576" + } + } + }, + "links": { + "self": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f9" + }, + "space": { + "href": "http://somewhere.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576" + }, + "processes": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f9/processes" + }, + "route_mappings": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f9/route_mappings" + }, + "packages": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f9/packages" + }, + "environment_variables": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f9/environment_variables" + }, + "current_droplet": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f9/droplets/current" + }, + "droplets": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f9/droplets" + }, + "tasks": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f9/tasks" + }, + "start": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f9/actions/start", + "method": "POST" + }, + "stop": { + "href": "http://somewhere.org/v3/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f9/actions/stop", + "method": "POST" + } + }, + "metadata": { + "labels": {} + } + } + ] +} \ No newline at end of file diff --git a/tests/operations/push/test_push.py b/tests/operations/push/test_push.py index 15cef3a..41321b6 100644 --- a/tests/operations/push/test_push.py +++ b/tests/operations/push/test_push.py @@ -46,7 +46,13 @@ def test_to_host_should_remove_unwanted_characters(self): @patch.object( sys, "argv", - ["main", "push_app", AbstractTestCase.get_fixtures_path("fake", "manifest_main.yml"), "-space_guid", "space_id"], + [ + "main", + "push_app", + AbstractTestCase.get_fixtures_path("fake", "operations", "manifest_main.yml"), + "-space_guid", + "space_id" + ], ) def test_main_push(self): class FakeOperation(object): @@ -59,4 +65,4 @@ def __init__(self): "cloudfoundry_client.main.operation_commands.PushOperation", new=lambda c: push_operation ): main.main() - push_operation.push.assert_called_with("space_id", self.get_fixtures_path("fake", "manifest_main.yml")) + push_operation.push.assert_called_with("space_id", self.get_fixtures_path("fake", "operations", "manifest_main.yml")) diff --git a/tests/v2/test_entities.py b/tests/v2/test_entities.py index 35427d8..d88920e 100644 --- a/tests/v2/test_entities.py +++ b/tests/v2/test_entities.py @@ -14,7 +14,7 @@ def test_invalid_entity_without_entity_attribute(self): entity_manager = EntityManager(self.TARGET_ENDPOINT, client, "/fake/anyone") client.get.return_value = self.mock_response( - "/fake/anyone/any-id", HTTPStatus.OK, None, "fake", "GET_invalid_entity_without_entity.json" + "/fake/anyone/any-id", HTTPStatus.OK, None, "v2", "fake", "GET_invalid_entity_without_entity.json" ) self.assertRaises(InvalidEntity, lambda: entity_manager["any-id"]) @@ -24,7 +24,7 @@ def test_invalid_entity_with_null_entity(self): entity_manager = EntityManager(self.TARGET_ENDPOINT, client, "/fake/anyone") client.get.return_value = self.mock_response( - "/fake/anyone/any-id", HTTPStatus.OK, None, "fake", "GET_invalid_entity_with_null_entity.json" + "/fake/anyone/any-id", HTTPStatus.OK, None, "v2", "fake", "GET_invalid_entity_with_null_entity.json" ) self.assertRaises(InvalidEntity, lambda: entity_manager["any-id"]) @@ -34,7 +34,7 @@ def test_invalid_entity_with_invalid_entity_type(self): entity_manager = EntityManager(self.TARGET_ENDPOINT, client, "/fake/anyone") client.get.return_value = self.mock_response( - "/fake/anyone/any-id", HTTPStatus.OK, None, "fake", "GET_invalid_entity_with_invalid_entity_type.json" + "/fake/anyone/any-id", HTTPStatus.OK, None, "v2", "fake", "GET_invalid_entity_with_invalid_entity_type.json" ) self.assertRaises(InvalidEntity, lambda: entity_manager["any-id"]) @@ -79,25 +79,29 @@ def test_list(self): "/fake/first?order-direction=asc&page=1&results-per-page=20&q=space_guid%3Asome-id", HTTPStatus.OK, None, - "fake", + "v2", "fake", "GET_multi_page_0_response.json", ) second_response = self.mock_response( "/fake/next?order-direction=asc&page=2&results-per-page=50", HTTPStatus.OK, None, - "fake", + "v2", "fake", "GET_multi_page_1_response.json", ) client.get.side_effect = [first_response, second_response] - cpt = reduce( - lambda increment, _: increment + 1, + guids = reduce( + lambda c, entity: c.append(entity["metadata"]["guid"]) or c, entity_manager.list(**{"results-per-page": 20, "order-direction": "asc", "page": 1, "space_guid": "some-id"}), - 0, + [], ) client.get.assert_has_calls([call(first_response.url), call(second_response.url)], any_order=False) - self.assertEqual(cpt, 3) + self.assertEqual(guids, [ + "6fa7a340-9bda-43bf-bd5e-4e588c292679", + "7002efa8-3f54-4338-8884-117e98f21566", + "774a9f7e-895d-4825-84fc-222c1522a9a7" + ]) def test_elements_are_entities(self): client = MagicMock() @@ -107,14 +111,14 @@ def test_elements_are_entities(self): "/fake/first?order-direction=asc&page=1&results-per-page=20&q=space_guid%3Asome-id", HTTPStatus.OK, None, - "fake", + "v2", "fake", "GET_multi_page_0_response.json", ) second_response = self.mock_response( "/fake/next?order-direction=asc&page=2&results-per-page=50", HTTPStatus.OK, None, - "fake", + "v2", "fake", "GET_multi_page_1_response.json", ) client.get.side_effect = [first_response, second_response] @@ -129,7 +133,7 @@ def test_iter(self): client = MagicMock() entity_manager = EntityManager(self.TARGET_ENDPOINT, client, "/fake/something") - client.get.return_value = self.mock_response("/fake/something", HTTPStatus.OK, None, "fake", "GET_response.json") + client.get.return_value = self.mock_response("/fake/something", HTTPStatus.OK, None, "v2", "fake", "GET_response.json") cpt = reduce(lambda increment, _: increment + 1, entity_manager, 0) client.get.assert_called_with(client.get.return_value.url) @@ -140,7 +144,7 @@ def test_get_elem(self): entity_manager = EntityManager(self.TARGET_ENDPOINT, client, "/fake/something") client.get.return_value = self.mock_response( - "/fake/something/with-id", HTTPStatus.OK, None, "fake", "GET_{id}_response.json" + "/fake/something/with-id", HTTPStatus.OK, None, "v2", "fake", "GET_{id}_response.json" ) entity = entity_manager["with-id"] client.get.assert_called_with(client.get.return_value.url) @@ -151,7 +155,7 @@ def test_entity_manager_is_a_generator(self): client = MagicMock() entity_manager = EntityManager(self.TARGET_ENDPOINT, client, "/fake/something") client.get.return_value = self.mock_response( - "/fake/something/with-id", HTTPStatus.OK, None, "fake", "GET_{id}_response.json" + "/fake/something/with-id", HTTPStatus.OK, None, "v2", "fake", "GET_{id}_response.json" ) self.assertIsNotNone(getattr(entity_manager, "__iter__", None)) @@ -164,7 +168,7 @@ def test_entity_list_is_a_generator(self): client = MagicMock() entity_manager = EntityManager(self.TARGET_ENDPOINT, client, "/fake/something") client.get.return_value = self.mock_response( - "/fake/something/with-id", HTTPStatus.OK, None, "fake", "GET_{id}_response.json" + "/fake/something/with-id", HTTPStatus.OK, None, "v2", "fake", "GET_{id}_response.json" ) generator = entity_manager.list() @@ -175,7 +179,7 @@ def test_entity_list_is_a_generator(self): def test_total_results(self): client = MagicMock() entity_manager = EntityManager(self.TARGET_ENDPOINT, client, "/fake/something") - client.get.return_value = self.mock_response("/fake/something", HTTPStatus.OK, None, "fake", "GET_response.json") + client.get.return_value = self.mock_response("/fake/something", HTTPStatus.OK, None, "v2", "fake", "GET_response.json") cpt = entity_manager.list().total_results diff --git a/tests/v3/test_entities.py b/tests/v3/test_entities.py index 4daee4f..65924ef 100644 --- a/tests/v3/test_entities.py +++ b/tests/v3/test_entities.py @@ -1,4 +1,5 @@ import unittest +from functools import reduce from http import HTTPStatus from unittest.mock import MagicMock @@ -49,3 +50,36 @@ def test_elements_are_entities(self): for entity in entities_list: self.assertIsInstance(entity, Entity) + + def test_list_pagination(self): + client = MagicMock() + + entity_manager = EntityManager(self.TARGET_ENDPOINT, client, "/fake") + first_response = self.mock_response( + "/fake", + HTTPStatus.OK, + None, + "v3", "fake", + "GET_multi_page_0_response.json", + ) + second_response = self.mock_response( + "/fake/last?order-direction=asc&page=2&results-per-page=50", + HTTPStatus.OK, + None, + "v3", "fake", + "GET_multi_page_1_response.json", + ) + + client.get.side_effect = [first_response, second_response] + + guids = reduce( + lambda c, entity: c.append(entity["guid"]) or c, + entity_manager.list(), + [], + ) + self.assertEqual(guids, [ + "1cb006ee-fb05-47e1-b541-c34179ddc446", + "02b4ec9b-94c7-4468-9c23-4e906191a0f8", + "1cb006ee-fb05-47e1-b541-c34179ddc447", + "02b4ec9b-94c7-4468-9c23-4e906191a0f9", + ]) From d834939ba5812f3833aec2861ddc291ae8dc71c4 Mon Sep 17 00:00:00 2001 From: Krzysztof <38418755+krismarc@users.noreply.github.com> Date: Wed, 7 Jan 2026 13:58:08 +0100 Subject: [PATCH 235/264] Take CF_HOME into account. (#281) Additional handling if config directory or file is provided as a config_path. Co-authored-by: Krzysztof Marciniak --- cloudfoundry_client/client.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/cloudfoundry_client/client.py b/cloudfoundry_client/client.py index 9163f1b..aa06573 100644 --- a/cloudfoundry_client/client.py +++ b/cloudfoundry_client/client.py @@ -1,4 +1,5 @@ import logging +import os from pathlib import Path import json from http import HTTPStatus @@ -247,7 +248,21 @@ def _get_info(self, target_endpoint: str, proxy: dict | None = None, verify: boo @staticmethod def build_from_cf_config(config_path: str | None = None, **kwargs) -> 'CloudFoundryClient': - config = Path(config_path) if config_path else Path.home() / '.cf/config.json' + cf_home = "CF_HOME" + config_file = "config.json" + config_dir = ".cf" + # handles config path provided or defaults to CF_HOME or HOME + if config_path is not None: + config = Path(config_path) + if config.is_dir(): + if config.name == config_dir: + config = config / config_file + else: + config = config / config_dir / config_file + elif os.environ.get(cf_home): + config = Path(os.environ.get(cf_home)) / config_dir / config_file + else: + config = Path.home() / config_dir / config_file try: with open(config) as f: cf_config = json.load(f) From 28bb291d12b592b61ecbebc6f4c437f214bc5db8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Jan 2026 17:38:19 +0100 Subject: [PATCH 236/264] chore(deps): bump urllib3 from 2.6.2 to 2.6.3 (#282) Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.6.2 to 2.6.3. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/2.6.2...2.6.3) --- updated-dependencies: - dependency-name: urllib3 dependency-version: 2.6.3 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index dee022f..fe7ecaa 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -1874,14 +1874,14 @@ markers = {main = "python_version < \"3.13\"", dev = "python_version == \"3.10\" [[package]] name = "urllib3" -version = "2.6.2" +version = "2.6.3" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" groups = ["main", "dev"] files = [ - {file = "urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd"}, - {file = "urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797"}, + {file = "urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4"}, + {file = "urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed"}, ] [package.extras] From 8c5bf261b185d6f5061903be43d61d3c36cd5777 Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Thu, 8 Jan 2026 18:29:46 +0100 Subject: [PATCH 237/264] feature(v3) add user v3 (#278) Closes #271 --- cloudfoundry_client/client.py | 2 + cloudfoundry_client/v3/buildpacks.py | 4 +- cloudfoundry_client/v3/domains.py | 2 +- cloudfoundry_client/v3/entities.py | 9 +++ cloudfoundry_client/v3/isolation_segments.py | 6 +- cloudfoundry_client/v3/organizations.py | 11 +-- cloudfoundry_client/v3/service_brokers.py | 16 +--- cloudfoundry_client/v3/service_instances.py | 20 +---- cloudfoundry_client/v3/service_offerings.py | 8 +- cloudfoundry_client/v3/service_plans.py | 6 +- cloudfoundry_client/v3/users.py | 40 ++++++++++ tests/fixtures/v3/users/GET_response.json | 50 +++++++++++++ .../fixtures/v3/users/GET_{id}_response.json | 17 +++++ .../v3/users/PATCH_{id}_response.json | 21 ++++++ tests/fixtures/v3/users/POST_response.json | 17 +++++ tests/v3/test_buildpacks.py | 2 - tests/v3/test_domains.py | 1 - tests/v3/test_isolation_segments.py | 2 +- tests/v3/test_organizations.py | 2 +- tests/v3/test_users.py | 73 +++++++++++++++++++ 20 files changed, 249 insertions(+), 60 deletions(-) create mode 100644 cloudfoundry_client/v3/users.py create mode 100644 tests/fixtures/v3/users/GET_response.json create mode 100644 tests/fixtures/v3/users/GET_{id}_response.json create mode 100644 tests/fixtures/v3/users/PATCH_{id}_response.json create mode 100644 tests/fixtures/v3/users/POST_response.json create mode 100644 tests/v3/test_users.py diff --git a/cloudfoundry_client/client.py b/cloudfoundry_client/client.py index aa06573..998acbe 100644 --- a/cloudfoundry_client/client.py +++ b/cloudfoundry_client/client.py @@ -45,6 +45,7 @@ from cloudfoundry_client.v3.spaces import SpaceManager from cloudfoundry_client.v3.tasks import TaskManager from cloudfoundry_client.v3.jobs import JobManager +from cloudfoundry_client.v3.users import UserManager _logger = logging.getLogger(__name__) @@ -131,6 +132,7 @@ def __init__(self, cloud_controller_v3_url: str, credential_manager: "CloudFound self.service_plans = ServicePlanManager(target_endpoint, credential_manager) self.spaces = SpaceManager(target_endpoint, credential_manager) self.tasks = TaskManager(target_endpoint, credential_manager) + self.users = UserManager(target_endpoint, credential_manager) class CloudFoundryClient(CredentialManager): diff --git a/cloudfoundry_client/v3/buildpacks.py b/cloudfoundry_client/v3/buildpacks.py index ba14c9a..a8b3e68 100644 --- a/cloudfoundry_client/v3/buildpacks.py +++ b/cloudfoundry_client/v3/buildpacks.py @@ -26,8 +26,8 @@ def create( "enabled": enabled, "locked": locked, "stack": stack, - "metadata": {"labels": meta_labels, "annotations": meta_annotations}, } + self._metadata(data, meta_labels, meta_annotations) return super(BuildpackManager, self)._create(data) def remove(self, buildpack_guid: str, asynchronous: bool = True) -> str | None: @@ -50,8 +50,8 @@ def update( "enabled": enabled, "locked": locked, "stack": stack, - "metadata": {"labels": meta_labels, "annotations": meta_annotations}, } + self._metadata(data, meta_labels, meta_annotations) return super(BuildpackManager, self)._update(buildpack_guid, data) def upload(self, buildpack_guid: str, buildpack_zip: str, asynchronous: bool = False) -> Entity: diff --git a/cloudfoundry_client/v3/domains.py b/cloudfoundry_client/v3/domains.py index 9068cd1..aa752a0 100644 --- a/cloudfoundry_client/v3/domains.py +++ b/cloudfoundry_client/v3/domains.py @@ -39,8 +39,8 @@ def create( "organization": organization, "shared_organizations": shared_organizations, }, - "metadata": {"labels": meta_labels, "annotations": meta_annotations}, } + self._metadata(data, meta_labels, meta_annotations) return super(DomainManager, self)._create(data) def list_domains_for_org(self, org_guid: str, **kwargs) -> Pagination[Entity]: diff --git a/cloudfoundry_client/v3/entities.py b/cloudfoundry_client/v3/entities.py index 6ddacd8..be2b257 100644 --- a/cloudfoundry_client/v3/entities.py +++ b/cloudfoundry_client/v3/entities.py @@ -312,3 +312,12 @@ def _append_encoded_parameter(parameters: list[str], args: tuple[str, Any]) -> l return "%s?%s" % (url, "&".join(functools.reduce(_append_encoded_parameter, sorted(list(kwargs.items())), []))) else: return url + + def _metadata(self, data, meta_labels, meta_annotations): + if meta_labels or meta_annotations: + metadata = dict() + if meta_labels: + metadata["labels"] = meta_labels + if meta_annotations: + metadata["annotations"] = meta_annotations + data["metadata"] = metadata diff --git a/cloudfoundry_client/v3/isolation_segments.py b/cloudfoundry_client/v3/isolation_segments.py index e11edb9..772e4cc 100644 --- a/cloudfoundry_client/v3/isolation_segments.py +++ b/cloudfoundry_client/v3/isolation_segments.py @@ -11,13 +11,15 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(IsolationSegmentManager, self).__init__(target_endpoint, client, "/v3/isolation_segments") def create(self, name: str, meta_labels: dict | None = None, meta_annotations: dict | None = None) -> Entity: - data = {"name": name, "metadata": {"labels": meta_labels, "annotations": meta_annotations}} + data = {"name": name} + self._metadata(data, meta_labels, meta_annotations) return super(IsolationSegmentManager, self)._create(data) def update( self, isolation_segment_guid: str, name: str, meta_labels: dict | None = None, meta_annotations: dict | None = None ) -> Entity: - data = {"name": name, "metadata": {"labels": meta_labels, "annotations": meta_annotations}} + data = {"name": name} + self._metadata(data, meta_labels, meta_annotations) return super(IsolationSegmentManager, self)._update(isolation_segment_guid, data) def entitle_organizations(self, isolation_segment_guid: str, *org_guids: str) -> ToManyRelationship: diff --git a/cloudfoundry_client/v3/organizations.py b/cloudfoundry_client/v3/organizations.py index 252411d..18562ee 100644 --- a/cloudfoundry_client/v3/organizations.py +++ b/cloudfoundry_client/v3/organizations.py @@ -13,7 +13,8 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): def create( self, name: str, suspended: bool, meta_labels: dict | None = None, meta_annotations: dict | None = None ) -> Entity: - data = {"name": name, "suspended": suspended, "metadata": {"labels": meta_labels, "annotations": meta_annotations}} + data = {"name": name, "suspended": suspended} + self._metadata(data, meta_labels, meta_annotations) return super(OrganizationManager, self)._create(data) def update( @@ -27,13 +28,7 @@ def update( data = {"name": name} if suspended is not None: data["suspended"] = suspended - metadata = {} - if meta_labels is not None: - metadata["labels"] = meta_labels - if meta_annotations is not None: - metadata["annotations"] = meta_annotations - if len(metadata) > 0: - data["metadata"] = metadata + self._metadata(data, meta_labels, meta_annotations) return super(OrganizationManager, self)._update(guid, data) def remove(self, guid: str, asynchronous: bool = True) -> str | None: diff --git a/cloudfoundry_client/v3/service_brokers.py b/cloudfoundry_client/v3/service_brokers.py index f1febc6..c507f18 100644 --- a/cloudfoundry_client/v3/service_brokers.py +++ b/cloudfoundry_client/v3/service_brokers.py @@ -22,13 +22,7 @@ def create( ) -> Entity: credentials = {"type": "basic", "credentials": {"username": auth_username, "password": auth_password}} payload = dict(name=name, url=url, authentication=credentials) - if meta_labels or meta_annotations: - metadata = dict() - if meta_labels: - metadata["labels"] = meta_labels - if meta_annotations: - metadata["annotations"] = meta_annotations - payload["metadata"] = metadata + self._metadata(payload, meta_labels, meta_annotations) if space_guid: payload["relationships"] = dict(space=ToOneRelationship(space_guid)) return super(ServiceBrokerManager, self)._create(payload) @@ -50,13 +44,7 @@ def update( payload["url"] = url if auth_username and auth_password: payload["authentication"] = {"type": "basic", "credentials": {"username": auth_username, "password": auth_password}} - if meta_labels or meta_annotations: - metadata = dict() - if meta_labels: - metadata["labels"] = meta_labels - if meta_annotations: - metadata["annotations"] = meta_annotations - payload["metadata"] = metadata + self._metadata(payload, meta_labels, meta_annotations) return super(ServiceBrokerManager, self)._update(guid, payload) def remove(self, guid: str, asynchronous: bool = True) -> str | None: diff --git a/cloudfoundry_client/v3/service_instances.py b/cloudfoundry_client/v3/service_instances.py index d882604..189870e 100644 --- a/cloudfoundry_client/v3/service_instances.py +++ b/cloudfoundry_client/v3/service_instances.py @@ -30,13 +30,7 @@ def create( data["parameters"] = parameters if tags: data["tags"] = tags - if meta_labels or meta_annotations: - metadata = dict() - if meta_labels: - metadata["labels"] = meta_labels - if meta_annotations: - metadata["annotations"] = meta_annotations - data["metadata"] = metadata + self._metadata(data, meta_labels, meta_annotations) return super(ServiceInstanceManager, self)._create(data) def update( @@ -62,17 +56,11 @@ def update( data["maintenance_info"] = {"version": maintenance_info} if tags: data["tags"] = tags - if meta_labels or meta_annotations: - metadata = dict() - if meta_labels: - metadata["labels"] = meta_labels - if meta_annotations: - metadata["annotations"] = meta_annotations - data["metadata"] = metadata - return super(ServiceInstanceManager, self)._update(instance_guid, data) + super()._metadata(data, meta_labels, meta_annotations) + return super()._update(instance_guid, data) def remove(self, guid: str, asynchronous: bool = True): - super(ServiceInstanceManager, self)._remove(guid, asynchronous) + super()._remove(guid, asynchronous) def get_permissions(self, instance_guid: str) -> JsonObject: return super(ServiceInstanceManager, self)._get( diff --git a/cloudfoundry_client/v3/service_offerings.py b/cloudfoundry_client/v3/service_offerings.py index c47ca66..bc067aa 100644 --- a/cloudfoundry_client/v3/service_offerings.py +++ b/cloudfoundry_client/v3/service_offerings.py @@ -12,13 +12,7 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): def update(self, guid: str, meta_labels: dict | None = None, meta_annotations: dict | None = None) -> Entity: payload = dict() - if meta_labels or meta_annotations: - metadata = dict() - if meta_labels: - metadata["labels"] = meta_labels - if meta_annotations: - metadata["annotations"] = meta_annotations - payload["metadata"] = metadata + self._metadata(payload, meta_labels, meta_annotations) return super(ServiceOfferingsManager, self)._update(guid, payload) def remove(self, guid: str, purge: bool = False) -> None: diff --git a/cloudfoundry_client/v3/service_plans.py b/cloudfoundry_client/v3/service_plans.py index f2333aa..0c6f56a 100644 --- a/cloudfoundry_client/v3/service_plans.py +++ b/cloudfoundry_client/v3/service_plans.py @@ -17,11 +17,7 @@ def update( meta_annotations: dict | None = None, ) -> Entity: payload = {"metadata": {}} - - if meta_labels: - payload["metadata"]["labels"] = meta_labels - if meta_annotations: - payload["metadata"]["annotations"] = meta_annotations + self._metadata(payload, meta_labels, meta_annotations) return super(ServicePlanManager, self)._update(guid, payload) def remove(self, guid: str): diff --git a/cloudfoundry_client/v3/users.py b/cloudfoundry_client/v3/users.py new file mode 100644 index 0000000..431bd19 --- /dev/null +++ b/cloudfoundry_client/v3/users.py @@ -0,0 +1,40 @@ +from typing import TYPE_CHECKING + +from cloudfoundry_client.v3.entities import Entity, EntityManager + +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + + +class UserManager(EntityManager): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(UserManager, self).__init__(target_endpoint, client, "/v3/users") + + def create( + self, + user_info: str | tuple[str, str], + meta_labels: dict | None = None, + meta_annotations: dict | None = None, + ) -> Entity: + data = {} + if isinstance(user_info, str): + data["guid"] = user_info + else: + username, origin = user_info + data["username"] = username + data["origin"] = origin + self._metadata(data, meta_labels, meta_annotations) + return super(UserManager, self)._create(data) + + def update( + self, + guid: str, + meta_labels: dict | None = None, + meta_annotations: dict | None = None, + ) -> Entity: + data = {} + self._metadata(data, meta_labels, meta_annotations) + return super(UserManager, self)._update(guid, data) + + def remove(self, guid: str) -> str | None: + return super(UserManager, self)._remove(guid) diff --git a/tests/fixtures/v3/users/GET_response.json b/tests/fixtures/v3/users/GET_response.json new file mode 100644 index 0000000..de19877 --- /dev/null +++ b/tests/fixtures/v3/users/GET_response.json @@ -0,0 +1,50 @@ +{ + "pagination": { + "total_results": 3, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/users?page=1&per_page=2" + }, + "last": { + "href": "https://api.example.org/v3/users?page=2&per_page=2" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "client_id", + "created_at": "2019-03-08T01:06:19Z", + "updated_at": "2019-03-08T01:06:19Z", + "username": null, + "presentation_name": "client_id", + "origin": null, + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/users/3a5d3d89-3f89-4f05-8188-8a2b298c79d5" + } + } + }, + { + "guid": "9da93b89-3f89-4f05-7238-8a2b123c79l9", + "created_at": "2019-03-08T01:06:19Z", + "updated_at": "2019-03-08T01:06:19Z", + "username": "some-name", + "presentation_name": "some-name", + "origin": "uaa", + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/users/9da93b89-3f89-4f05-7238-8a2b123c79l9" + } + } + } + ] +} diff --git a/tests/fixtures/v3/users/GET_{id}_response.json b/tests/fixtures/v3/users/GET_{id}_response.json new file mode 100644 index 0000000..b60ce5a --- /dev/null +++ b/tests/fixtures/v3/users/GET_{id}_response.json @@ -0,0 +1,17 @@ +{ + "guid": "3a5d3d89-3f89-4f05-8188-8a2b298c79d5", + "created_at": "2019-03-08T01:06:19Z", + "updated_at": "2019-03-08T01:06:19Z", + "username": "some-name", + "presentation_name": "some-name", + "origin": "uaa", + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/users/3a5d3d89-3f89-4f05-8188-8a2b298c79d5" + } + } +} \ No newline at end of file diff --git a/tests/fixtures/v3/users/PATCH_{id}_response.json b/tests/fixtures/v3/users/PATCH_{id}_response.json new file mode 100644 index 0000000..d516fb4 --- /dev/null +++ b/tests/fixtures/v3/users/PATCH_{id}_response.json @@ -0,0 +1,21 @@ +{ + "guid": "3a5d3d89-3f89-4f05-8188-8a2b298c79d5", + "created_at": "2019-03-08T01:06:19Z", + "updated_at": "2019-03-08T01:06:19Z", + "username": "some-name", + "presentation_name": "some-name", + "origin": "uaa", + "metadata": { + "labels": { + "enviroment": "production" + }, + "annotations": { + "note": "detailed information" + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/users/3a5d3d89-3f89-4f05-8188-8a2b298c79d5" + } + } +} diff --git a/tests/fixtures/v3/users/POST_response.json b/tests/fixtures/v3/users/POST_response.json new file mode 100644 index 0000000..b60ce5a --- /dev/null +++ b/tests/fixtures/v3/users/POST_response.json @@ -0,0 +1,17 @@ +{ + "guid": "3a5d3d89-3f89-4f05-8188-8a2b298c79d5", + "created_at": "2019-03-08T01:06:19Z", + "updated_at": "2019-03-08T01:06:19Z", + "username": "some-name", + "presentation_name": "some-name", + "origin": "uaa", + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/users/3a5d3d89-3f89-4f05-8188-8a2b298c79d5" + } + } +} \ No newline at end of file diff --git a/tests/v3/test_buildpacks.py b/tests/v3/test_buildpacks.py index d7d9ba5..cec5228 100644 --- a/tests/v3/test_buildpacks.py +++ b/tests/v3/test_buildpacks.py @@ -47,7 +47,6 @@ def test_update(self): "enabled": True, "position": 42, "stack": "windows64", - "metadata": {"labels": None, "annotations": None}, }, ) self.assertIsNotNone(result) @@ -66,7 +65,6 @@ def test_create(self): "enabled": True, "position": 42, "stack": "windows64", - "metadata": {"labels": None, "annotations": None}, }, ) self.assertIsNotNone(result) diff --git a/tests/v3/test_domains.py b/tests/v3/test_domains.py index 4e6c8dc..57a28f9 100644 --- a/tests/v3/test_domains.py +++ b/tests/v3/test_domains.py @@ -76,7 +76,6 @@ def test_create(self): ] }, }, - "metadata": {"labels": None, "annotations": None}, }, ) self.assertIsNotNone(result) diff --git a/tests/v3/test_isolation_segments.py b/tests/v3/test_isolation_segments.py index 2c26bf1..344da89 100644 --- a/tests/v3/test_isolation_segments.py +++ b/tests/v3/test_isolation_segments.py @@ -51,7 +51,7 @@ def test_update(self): result = self.client.v3.isolation_segments.update("isolation_segment_id", "new-name", meta_labels=dict(key="value")) self.client.patch.assert_called_with( self.client.patch.return_value.url, - json={"name": "new-name", "metadata": {"labels": {"key": "value"}, "annotations": None}}, + json={"name": "new-name", "metadata": {"labels": {"key": "value"}}}, ) self.assertIsNotNone(result) self.assertIsInstance(result, Entity) diff --git a/tests/v3/test_organizations.py b/tests/v3/test_organizations.py index edc08b4..e8c3f73 100644 --- a/tests/v3/test_organizations.py +++ b/tests/v3/test_organizations.py @@ -78,7 +78,7 @@ def test_create(self): self.client.post.assert_called_with( self.client.post.return_value.url, files=None, - json={"name": "my-organization", "suspended": False, "metadata": {"labels": None, "annotations": None}}, + json={"name": "my-organization", "suspended": False}, ) self.assertIsNotNone(result) diff --git a/tests/v3/test_users.py b/tests/v3/test_users.py new file mode 100644 index 0000000..a295e1e --- /dev/null +++ b/tests/v3/test_users.py @@ -0,0 +1,73 @@ +import unittest +from http import HTTPStatus + +from abstract_test_case import AbstractTestCase +from cloudfoundry_client.v3.entities import Entity + + +class TestUsers(unittest.TestCase, AbstractTestCase): + @classmethod + def setUpClass(cls): + cls.mock_client_class() + + def setUp(self): + self.build_client() + + def test_create(self): + self.client.post.return_value = self.mock_response( + "/v3/users", HTTPStatus.OK, None, "v3", "users", "POST_response.json" + ) + result = self.client.v3.users.create("3a5d3d89-3f89-4f05-8188-8a2b298c79d5") + self.client.post.assert_called_with( + self.client.post.return_value.url, + files=None, + json={"guid": "3a5d3d89-3f89-4f05-8188-8a2b298c79d5"}, + ) + self.assertIsNotNone(result) + self.assertIsInstance(result, Entity) + + def test_list(self): + self.client.get.return_value = self.mock_response( + "/v3/users", HTTPStatus.OK, None, "v3", "users", "GET_response.json" + ) + all_users = [user for user in self.client.v3.users.list()] + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual(2, len(all_users)) + self.assertEqual(all_users[0]["guid"], "client_id") + self.assertIsInstance(all_users[0], Entity) + + def test_get(self): + self.client.get.return_value = self.mock_response( + "/v3/users/user_id", HTTPStatus.OK, None, "v3", "users", "GET_{id}_response.json" + ) + user = self.client.v3.users.get("user_id") + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual("uaa", user["origin"]) + self.assertIsInstance(user, Entity) + + def test_update(self): + self.client.patch.return_value = self.mock_response( + "/v3/users/user_id", HTTPStatus.OK, None, "v3", "users", "PATCH_{id}_response.json" + ) + result = self.client.v3.users.update( + "user_id", + {"environment": "production"}, + {"note": "detailed information"} + ) + + self.client.patch.assert_called_with( + self.client.patch.return_value.url, + json={ + "metadata": { + "labels": {"environment": "production"}, + "annotations": {"note": "detailed information"} + } + }, + ) + self.assertIsNotNone(result) + self.assertIsInstance(result, Entity) + + def test_remove(self): + self.client.delete.return_value = self.mock_response("/v3/users/user_id", HTTPStatus.NO_CONTENT, None) + self.client.v3.users.remove("user_id") + self.client.delete.assert_called_with(self.client.delete.return_value.url) From e1f1805cac2b531bee9e0e1b155c168b7f53ee42 Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Thu, 8 Jan 2026 18:40:57 +0100 Subject: [PATCH 238/264] chore(code) better use of generic in entity manager (#283) --- cloudfoundry_client/v3/apps.py | 2 +- cloudfoundry_client/v3/buildpacks.py | 2 +- cloudfoundry_client/v3/domains.py | 2 +- cloudfoundry_client/v3/entities.py | 72 +++++++++++-------- cloudfoundry_client/v3/feature_flags.py | 2 +- cloudfoundry_client/v3/isolation_segments.py | 2 +- cloudfoundry_client/v3/jobs.py | 2 +- cloudfoundry_client/v3/organization_quotas.py | 2 +- cloudfoundry_client/v3/organizations.py | 2 +- cloudfoundry_client/v3/processes.py | 4 +- cloudfoundry_client/v3/roles.py | 4 +- cloudfoundry_client/v3/security_groups.py | 4 +- cloudfoundry_client/v3/service_brokers.py | 2 +- .../v3/service_credential_bindings.py | 2 +- cloudfoundry_client/v3/service_instances.py | 2 +- cloudfoundry_client/v3/service_offerings.py | 2 +- cloudfoundry_client/v3/service_plans.py | 2 +- cloudfoundry_client/v3/spaces.py | 2 +- cloudfoundry_client/v3/tasks.py | 2 +- cloudfoundry_client/v3/users.py | 2 +- 20 files changed, 66 insertions(+), 50 deletions(-) diff --git a/cloudfoundry_client/v3/apps.py b/cloudfoundry_client/v3/apps.py index 01746d4..46b9dce 100644 --- a/cloudfoundry_client/v3/apps.py +++ b/cloudfoundry_client/v3/apps.py @@ -7,7 +7,7 @@ from cloudfoundry_client.client import CloudFoundryClient -class AppManager(EntityManager): +class AppManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(AppManager, self).__init__(target_endpoint, client, "/v3/apps") diff --git a/cloudfoundry_client/v3/buildpacks.py b/cloudfoundry_client/v3/buildpacks.py index a8b3e68..aa88ab8 100644 --- a/cloudfoundry_client/v3/buildpacks.py +++ b/cloudfoundry_client/v3/buildpacks.py @@ -6,7 +6,7 @@ from cloudfoundry_client.client import CloudFoundryClient -class BuildpackManager(EntityManager): +class BuildpackManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(BuildpackManager, self).__init__(target_endpoint, client, "/v3/buildpacks") diff --git a/cloudfoundry_client/v3/domains.py b/cloudfoundry_client/v3/domains.py index aa752a0..a26bea9 100644 --- a/cloudfoundry_client/v3/domains.py +++ b/cloudfoundry_client/v3/domains.py @@ -19,7 +19,7 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient", **kwargs) ) -class DomainManager(EntityManager): +class DomainManager(EntityManager[Domain]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(DomainManager, self).__init__(target_endpoint, client, "/v3/domains", Domain) diff --git a/cloudfoundry_client/v3/entities.py b/cloudfoundry_client/v3/entities.py index be2b257..8a7ba6f 100644 --- a/cloudfoundry_client/v3/entities.py +++ b/cloudfoundry_client/v3/entities.py @@ -1,7 +1,7 @@ import functools from collections.abc import Callable from json import JSONDecodeError -from typing import Any, TypeVar, TYPE_CHECKING, Type +from typing import Any, TypeVar, TYPE_CHECKING, Type, Generic from urllib.parse import quote, urlparse from requests import Response @@ -111,30 +111,42 @@ def __init__(self, *guids: str): self.guids = list(guids) -ENTITY_TYPE = TypeVar("ENTITY_TYPE", bound=Entity) +ENTITY_TYPE = TypeVar("ENTITY_TYPE", bound=Entity, covariant=True) -class EntityManager(object): - def __init__(self, target_endpoint: str, client: "CloudFoundryClient", entity_uri: str, entity_type: ENTITY_TYPE = Entity): +class EntityManager(Generic[ENTITY_TYPE]): + def __init__( + self, + target_endpoint: str, + client: "CloudFoundryClient", + entity_uri: str, + entity_type: type[ENTITY_TYPE] = Entity + ): self.target_endpoint = target_endpoint self.entity_uri = entity_uri self.client = client self.entity_type = entity_type - def _post(self, url: str, data: dict | None = None, files: Any = None, entity_type: ENTITY_TYPE = None) -> Entity: - response = self.client.post(url, json=data, files=files) - return self._read_response(response, entity_type) - - def _get(self, url: str, entity_type: ENTITY_TYPE | None = None, **kwargs) -> Entity: + def _get(self, url: str, entity_type: type[ENTITY_TYPE] | None = None, **kwargs) -> ENTITY_TYPE: url_requested = EntityManager._get_url_with_encoded_params(url, **kwargs) response = self.client.get(url_requested) return self._read_response(response, entity_type) - def _put(self, url: str, data: dict, entity_type: ENTITY_TYPE | None = None) -> Entity: + def _post( + self, + url: str, + data: dict | None = None, + entity_type: type[ENTITY_TYPE] | None = None, + files: Any = None + ) -> ENTITY_TYPE: + response = self.client.post(url, json=data, files=files) + return self._read_response(response, entity_type) + + def _put(self, url: str, data: dict, entity_type: type[ENTITY_TYPE] | None = None) -> ENTITY_TYPE: response = self.client.put(url, json=data) return self._read_response(response, entity_type) - def _patch(self, url: str, data: dict, entity_type: ENTITY_TYPE | None = None) -> Entity: + def _patch(self, url: str, data: dict, entity_type: type[ENTITY_TYPE] | None = None) -> ENTITY_TYPE: response = self.client.patch(url, json=data) return self._read_response(response, entity_type) @@ -166,21 +178,21 @@ def _extract_job_guid(job_location): job_guid = job_url.path.rsplit("/", 1)[-1] return job_guid - def _list(self, requested_path: str, entity_type: ENTITY_TYPE | None = None, **kwargs) -> Pagination[Entity]: + def _list(self, requested_path: str, entity_type: type[ENTITY_TYPE] | None = None, **kwargs) -> Pagination[ENTITY_TYPE]: url_requested = EntityManager._get_url_with_encoded_params("%s%s" % (self.target_endpoint, requested_path), **kwargs) response_json = self._read_response(self.client.get(url_requested), JsonObject) return self._pagination(response_json, entity_type) - def _attempt_to_paginate(self, url_requested: str, entity_type: ENTITY_TYPE | None = None) \ - -> Pagination[Entity] | Entity: + def _attempt_to_paginate(self, url_requested: str, entity_type: type[ENTITY_TYPE] | None = None) \ + -> Pagination[ENTITY_TYPE] | ENTITY_TYPE: response_json = self._read_response(self.client.get(url_requested), JsonObject) if "resources" in response_json: return self._pagination(response_json, entity_type) else: return response_json - def _pagination(self, page: JsonObject, entity_type: ENTITY_TYPE | None = None) -> Pagination[Entity]: - def _entity(json_object: JsonObject) -> Entity: + def _pagination(self, page: JsonObject, entity_type: type[ENTITY_TYPE] | None = None) -> Pagination[ENTITY_TYPE]: + def _entity(json_object: JsonObject) -> ENTITY_TYPE: return self._entity(json_object, entity_type) return Pagination(page, @@ -200,23 +212,23 @@ def _next_page(self, current_page: JsonObject) -> JsonObject | None: return None return self._read_response(self.client.get(current_page["pagination"]["next"]["href"]), JsonObject) - def _create(self, data: dict) -> Entity: + def _create(self, data: dict) -> ENTITY_TYPE: url = "%s%s" % (self.target_endpoint, self.entity_uri) return self._post(url, data=data) - def _upload_bits(self, resource_id: str, filename: str) -> Entity: + def _upload_bits(self, resource_id: str, filename: str) -> ENTITY_TYPE: url = "%s%s/%s/upload" % (self.target_endpoint, self.entity_uri, resource_id) files = {"bits": (filename, open(filename, "rb"))} return self._post(url, files=files) - def _update(self, resource_id: str, data: dict) -> Entity: + def _update(self, resource_id: str, data: dict) -> ENTITY_TYPE: url = "%s%s/%s" % (self.target_endpoint, self.entity_uri, resource_id) return self._patch(url, data) - def __iter__(self) -> Pagination[Entity]: + def __iter__(self) -> Pagination[ENTITY_TYPE]: return self.list() - def __getitem__(self, entity_guid) -> Entity: + def __getitem__(self, entity_guid) -> ENTITY_TYPE: return self.get(entity_guid) def __len__(self): @@ -224,30 +236,34 @@ def __len__(self): def len(self, **kwargs): url_requested = EntityManager._get_url_with_encoded_params("%s%s" % (self.target_endpoint, self.entity_uri), **kwargs) - response_json = self._read_response(self.client.get(url_requested, JsonObject)) + response_json = self._read_response(self.client.get(url_requested), JsonObject) pagination = response_json.get("pagination") if pagination is not None: return pagination.get("total_results", 0) else: return 0 - def list(self, **kwargs) -> Pagination[Entity]: + def list(self, **kwargs) -> Pagination[ENTITY_TYPE]: return self._list(self.entity_uri, **kwargs) - def get_first(self, **kwargs) -> Entity | None: + def get_first(self, **kwargs) -> ENTITY_TYPE | None: kwargs.setdefault("per_page", 1) for entity in self._list(self.entity_uri, **kwargs): return entity return None - def get(self, entity_id: str, *extra_paths, **kwargs) -> Entity: + def get(self, entity_id: str, *extra_paths, **kwargs) -> ENTITY_TYPE: if len(extra_paths) == 0: requested_path = "%s%s/%s" % (self.target_endpoint, self.entity_uri, entity_id) else: requested_path = "%s%s/%s/%s" % (self.target_endpoint, self.entity_uri, entity_id, "/".join(extra_paths)) return self._get(requested_path, **kwargs) - def _read_response(self, response: Response, entity_type: ENTITY_TYPE | None) -> JsonObject | Entity: + def _read_response( + self, + response: Response, + entity_type: type[ENTITY_TYPE] | type[JsonObject] | None + ) -> JsonObject | ENTITY_TYPE: try: result = response.json(object_pairs_hook=JsonObject) except JSONDecodeError: @@ -289,7 +305,7 @@ def _include_resources(self, resource: JsonObject, result: JsonObject) -> None: def _get_entity_type(entity_name: str) -> Type[ENTITY_TYPE]: return Entity - def _entity(self, result: JsonObject, entity_type: ENTITY_TYPE | None) -> JsonObject | Entity: + def _entity(self, result: JsonObject, entity_type: type[ENTITY_TYPE] | None) -> JsonObject | ENTITY_TYPE: if "guid" in result or ("links" in result and "job" in result["links"]): return (entity_type or self.entity_type)(self.target_endpoint, self.client, **result) else: @@ -301,7 +317,7 @@ def _append_encoded_parameter(parameters: list[str], args: tuple[str, Any]) -> l parameter_name, parameter_value = args[0], args[1] if isinstance(parameter_value, (list, tuple)): parameters.append("%s=%s" % (parameter_name, quote(",".join(parameter_value)))) - elif isinstance(parameter_value, (dict)) and parameter_name == "fields": + elif isinstance(parameter_value, dict) and parameter_name == "fields": for resource, key in parameter_value.items(): parameters.append("%s[%s]=%s" % (parameter_name, resource, ",".join(key))) else: diff --git a/cloudfoundry_client/v3/feature_flags.py b/cloudfoundry_client/v3/feature_flags.py index c75517d..55717fb 100644 --- a/cloudfoundry_client/v3/feature_flags.py +++ b/cloudfoundry_client/v3/feature_flags.py @@ -6,7 +6,7 @@ from cloudfoundry_client.client import CloudFoundryClient -class FeatureFlagManager(EntityManager): +class FeatureFlagManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(FeatureFlagManager, self).__init__(target_endpoint, client, "/v3/feature_flags") diff --git a/cloudfoundry_client/v3/isolation_segments.py b/cloudfoundry_client/v3/isolation_segments.py index 772e4cc..eafd874 100644 --- a/cloudfoundry_client/v3/isolation_segments.py +++ b/cloudfoundry_client/v3/isolation_segments.py @@ -6,7 +6,7 @@ from cloudfoundry_client.client import CloudFoundryClient -class IsolationSegmentManager(EntityManager): +class IsolationSegmentManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(IsolationSegmentManager, self).__init__(target_endpoint, client, "/v3/isolation_segments") diff --git a/cloudfoundry_client/v3/jobs.py b/cloudfoundry_client/v3/jobs.py index 80bd510..9988886 100644 --- a/cloudfoundry_client/v3/jobs.py +++ b/cloudfoundry_client/v3/jobs.py @@ -13,7 +13,7 @@ class JobTimeout(Exception): pass -class JobManager(EntityManager): +class JobManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(JobManager, self).__init__(target_endpoint, client, "/v3/jobs") diff --git a/cloudfoundry_client/v3/organization_quotas.py b/cloudfoundry_client/v3/organization_quotas.py index 51f4b56..9304630 100644 --- a/cloudfoundry_client/v3/organization_quotas.py +++ b/cloudfoundry_client/v3/organization_quotas.py @@ -33,7 +33,7 @@ class DomainsQuota: total_domains: int -class OrganizationQuotaManager(EntityManager): +class OrganizationQuotaManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super().__init__(target_endpoint, client, "/v3/organization_quotas") diff --git a/cloudfoundry_client/v3/organizations.py b/cloudfoundry_client/v3/organizations.py index 18562ee..7dbacc4 100644 --- a/cloudfoundry_client/v3/organizations.py +++ b/cloudfoundry_client/v3/organizations.py @@ -6,7 +6,7 @@ from cloudfoundry_client.client import CloudFoundryClient -class OrganizationManager(EntityManager): +class OrganizationManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(OrganizationManager, self).__init__(target_endpoint, client, "/v3/organizations") diff --git a/cloudfoundry_client/v3/processes.py b/cloudfoundry_client/v3/processes.py index cb632f3..34af872 100644 --- a/cloudfoundry_client/v3/processes.py +++ b/cloudfoundry_client/v3/processes.py @@ -1,11 +1,11 @@ from typing import TYPE_CHECKING -from cloudfoundry_client.v3.entities import EntityManager +from cloudfoundry_client.v3.entities import EntityManager, Entity if TYPE_CHECKING: from cloudfoundry_client.client import CloudFoundryClient -class ProcessManager(EntityManager): +class ProcessManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(ProcessManager, self).__init__(target_endpoint, client, "/v3/processes") diff --git a/cloudfoundry_client/v3/roles.py b/cloudfoundry_client/v3/roles.py index d7f00c2..11e8eba 100644 --- a/cloudfoundry_client/v3/roles.py +++ b/cloudfoundry_client/v3/roles.py @@ -1,12 +1,12 @@ from typing import TYPE_CHECKING -from cloudfoundry_client.v3.entities import EntityManager +from cloudfoundry_client.v3.entities import EntityManager, Entity if TYPE_CHECKING: from cloudfoundry_client.client import CloudFoundryClient -class RoleManager(EntityManager): +class RoleManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(RoleManager, self).__init__(target_endpoint, client, "/v3/roles") diff --git a/cloudfoundry_client/v3/security_groups.py b/cloudfoundry_client/v3/security_groups.py index dcbc459..6a21459 100644 --- a/cloudfoundry_client/v3/security_groups.py +++ b/cloudfoundry_client/v3/security_groups.py @@ -35,7 +35,7 @@ class GloballyEnabled: staging: bool | None = None -class SecurityGroupManager(EntityManager): +class SecurityGroupManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(SecurityGroupManager, self).__init__(target_endpoint, client, "/v3/security_groups") @@ -82,7 +82,7 @@ def unbind_staging_security_group_from_space(self, security_group_id: str, space def _bind_spaces(self, security_group_id: str, space_guids: ToManyRelationship, relationship: str) \ -> ToManyRelationship: url = "%s%s/%s/relationships/%s" % (self.target_endpoint, self.entity_uri, security_group_id, relationship) - return ToManyRelationship.from_json_object(super()._post(url, space_guids)) + return ToManyRelationship.from_json_object(super()._post(url, data=space_guids)) def _unbind_space(self, security_group_id: str, space_guid: ToOneRelationship, relationship: str): url = "%s%s/%s/relationships/%s/%s" \ diff --git a/cloudfoundry_client/v3/service_brokers.py b/cloudfoundry_client/v3/service_brokers.py index c507f18..14e15e7 100644 --- a/cloudfoundry_client/v3/service_brokers.py +++ b/cloudfoundry_client/v3/service_brokers.py @@ -6,7 +6,7 @@ from cloudfoundry_client.client import CloudFoundryClient -class ServiceBrokerManager(EntityManager): +class ServiceBrokerManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(ServiceBrokerManager, self).__init__(target_endpoint, client, "/v3/service_brokers") diff --git a/cloudfoundry_client/v3/service_credential_bindings.py b/cloudfoundry_client/v3/service_credential_bindings.py index ff41c00..b1f53d5 100644 --- a/cloudfoundry_client/v3/service_credential_bindings.py +++ b/cloudfoundry_client/v3/service_credential_bindings.py @@ -6,7 +6,7 @@ from cloudfoundry_client.client import CloudFoundryClient -class ServiceCredentialBindingManager(EntityManager): +class ServiceCredentialBindingManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(ServiceCredentialBindingManager, self).__init__(target_endpoint, client, "/v3/service_credential_bindings") diff --git a/cloudfoundry_client/v3/service_instances.py b/cloudfoundry_client/v3/service_instances.py index 189870e..0d3d5d4 100644 --- a/cloudfoundry_client/v3/service_instances.py +++ b/cloudfoundry_client/v3/service_instances.py @@ -7,7 +7,7 @@ from cloudfoundry_client.client import CloudFoundryClient -class ServiceInstanceManager(EntityManager): +class ServiceInstanceManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(ServiceInstanceManager, self).__init__(target_endpoint, client, "/v3/service_instances") diff --git a/cloudfoundry_client/v3/service_offerings.py b/cloudfoundry_client/v3/service_offerings.py index bc067aa..76ebc7a 100644 --- a/cloudfoundry_client/v3/service_offerings.py +++ b/cloudfoundry_client/v3/service_offerings.py @@ -6,7 +6,7 @@ from cloudfoundry_client.client import CloudFoundryClient -class ServiceOfferingsManager(EntityManager): +class ServiceOfferingsManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(ServiceOfferingsManager, self).__init__(target_endpoint, client, "/v3/service_offerings") diff --git a/cloudfoundry_client/v3/service_plans.py b/cloudfoundry_client/v3/service_plans.py index 0c6f56a..3f4f6fc 100644 --- a/cloudfoundry_client/v3/service_plans.py +++ b/cloudfoundry_client/v3/service_plans.py @@ -6,7 +6,7 @@ from cloudfoundry_client.client import CloudFoundryClient -class ServicePlanManager(EntityManager): +class ServicePlanManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(ServicePlanManager, self).__init__(target_endpoint, client, "/v3/service_plans") diff --git a/cloudfoundry_client/v3/spaces.py b/cloudfoundry_client/v3/spaces.py index fd78d68..ec5751e 100644 --- a/cloudfoundry_client/v3/spaces.py +++ b/cloudfoundry_client/v3/spaces.py @@ -6,7 +6,7 @@ from cloudfoundry_client.client import CloudFoundryClient -class SpaceManager(EntityManager): +class SpaceManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(SpaceManager, self).__init__(target_endpoint, client, "/v3/spaces") diff --git a/cloudfoundry_client/v3/tasks.py b/cloudfoundry_client/v3/tasks.py index c5b23d5..0cda445 100644 --- a/cloudfoundry_client/v3/tasks.py +++ b/cloudfoundry_client/v3/tasks.py @@ -6,7 +6,7 @@ from cloudfoundry_client.client import CloudFoundryClient -class TaskManager(EntityManager): +class TaskManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(TaskManager, self).__init__(target_endpoint, client, "/v3/tasks") diff --git a/cloudfoundry_client/v3/users.py b/cloudfoundry_client/v3/users.py index 431bd19..cadc48f 100644 --- a/cloudfoundry_client/v3/users.py +++ b/cloudfoundry_client/v3/users.py @@ -6,7 +6,7 @@ from cloudfoundry_client.client import CloudFoundryClient -class UserManager(EntityManager): +class UserManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(UserManager, self).__init__(target_endpoint, client, "/v3/users") From bedfb19c2962ac223d318ecb3ec8cf64e5f68aea Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Fri, 9 Jan 2026 21:08:44 +0100 Subject: [PATCH 239/264] feature(routes) add route manager v3 (#284) See #280 --- cloudfoundry_client/client.py | 6 +- cloudfoundry_client/v3/routes.py | 58 +++++++ tests/fixtures/v3/routes/GET_response.json | 88 +++++++++++ .../fixtures/v3/routes/GET_{id}_response.json | 73 +++++++++ .../v3/routes/PATCH_{id}_response.json | 77 ++++++++++ tests/fixtures/v3/routes/POST_response.json | 73 +++++++++ tests/v3/test_routes.py | 142 ++++++++++++++++++ 7 files changed, 515 insertions(+), 2 deletions(-) create mode 100644 cloudfoundry_client/v3/routes.py create mode 100644 tests/fixtures/v3/routes/GET_response.json create mode 100644 tests/fixtures/v3/routes/GET_{id}_response.json create mode 100644 tests/fixtures/v3/routes/PATCH_{id}_response.json create mode 100644 tests/fixtures/v3/routes/POST_response.json create mode 100644 tests/v3/test_routes.py diff --git a/cloudfoundry_client/client.py b/cloudfoundry_client/client.py index 998acbe..80212ae 100644 --- a/cloudfoundry_client/client.py +++ b/cloudfoundry_client/client.py @@ -18,7 +18,7 @@ from cloudfoundry_client.v2.events import EventManager from cloudfoundry_client.v2.jobs import JobManager as JobManagerV2 from cloudfoundry_client.v2.resources import ResourceManager -from cloudfoundry_client.v2.routes import RouteManager +from cloudfoundry_client.v2.routes import RouteManager as RouteManagerV2 from cloudfoundry_client.v2.service_bindings import ServiceBindingManager from cloudfoundry_client.v2.service_brokers import ServiceBrokerManager as ServiceBrokerManagerV2 from cloudfoundry_client.v2.service_instances import ServiceInstanceManager as ServiceInstanceManagerV2 @@ -36,6 +36,7 @@ from cloudfoundry_client.v3.processes import ProcessManager from cloudfoundry_client.v3.organizations import OrganizationManager from cloudfoundry_client.v3.roles import RoleManager +from cloudfoundry_client.v3.routes import RouteManager from cloudfoundry_client.v3.security_groups import SecurityGroupManager from cloudfoundry_client.v3.service_brokers import ServiceBrokerManager from cloudfoundry_client.v3.service_credential_bindings import ServiceCredentialBindingManager @@ -97,7 +98,7 @@ def __init__(self, cloud_controller_v2_url: str, credential_manager: "CloudFound self.event = EventManager(target_endpoint, credential_manager) self.organizations = EntityManagerV2(target_endpoint, credential_manager, "/v2/organizations") self.private_domains = EntityManagerV2(target_endpoint, credential_manager, "/v2/private_domains") - self.routes = RouteManager(target_endpoint, credential_manager) + self.routes = RouteManagerV2(target_endpoint, credential_manager) self.services = EntityManagerV2(target_endpoint, credential_manager, "/v2/services") self.shared_domains = EntityManagerV2(target_endpoint, credential_manager, "/v2/shared_domains") self.spaces = SpaceManagerV2(target_endpoint, credential_manager) @@ -124,6 +125,7 @@ def __init__(self, cloud_controller_v3_url: str, credential_manager: "CloudFound self.organization_quotas = OrganizationQuotaManager(target_endpoint, credential_manager) self.processes = ProcessManager(target_endpoint, credential_manager) self.roles = RoleManager(target_endpoint, credential_manager) + self.routes = RouteManager(target_endpoint, credential_manager) self.security_groups = SecurityGroupManager(target_endpoint, credential_manager) self.service_brokers = ServiceBrokerManager(target_endpoint, credential_manager) self.service_credential_bindings = ServiceCredentialBindingManager(target_endpoint, credential_manager) diff --git a/cloudfoundry_client/v3/routes.py b/cloudfoundry_client/v3/routes.py new file mode 100644 index 0000000..4063971 --- /dev/null +++ b/cloudfoundry_client/v3/routes.py @@ -0,0 +1,58 @@ +from enum import Enum +from typing import TYPE_CHECKING, Any + +from cloudfoundry_client.v3.entities import EntityManager, Entity, ToOneRelationship + +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + + +class LoadBalancing(Enum): + ROUND_ROBIN = 'round-robin' + LEAST_CONNECTION = 'least-connection' + + +class RouteManager(EntityManager[Entity]): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(RouteManager, self).__init__(target_endpoint, client, "/v3/routes") + + def create(self, + space_guid: str, + domain_guid: str, + host: str | None = None, + path: str | None = None, + port: int | None = None, + load_balancing: LoadBalancing | None = None, + meta_labels: dict | None = None, + meta_annotations: dict | None = None, + ) -> Entity: + data: dict[str, Any] = { + "relationships": { + "space": ToOneRelationship(space_guid), "domain": ToOneRelationship(domain_guid) + }, + } + if host is not None: + data["host"] = host + if port is not None: + data["port"] = port + if path is not None: + data["path"] = path + if load_balancing is not None: + data["options"] = {"loadbalancing": load_balancing.value} + self._metadata(data, meta_labels, meta_annotations) + return super(RouteManager, self)._create(data) + + def update(self, + route_gid: str, + load_balancing: LoadBalancing | None = None, + meta_labels: dict | None = None, + meta_annotations: dict | None = None, + ) -> Entity: + data: dict[str, Any] = {} + if load_balancing is not None: + data["options"] = {"loadbalancing": load_balancing.value} + self._metadata(data, meta_labels, meta_annotations) + return super(RouteManager, self)._update(route_gid, data) + + def remove(self, route_gid: str): + return super(RouteManager, self)._remove(route_gid) diff --git a/tests/fixtures/v3/routes/GET_response.json b/tests/fixtures/v3/routes/GET_response.json new file mode 100644 index 0000000..680e80e --- /dev/null +++ b/tests/fixtures/v3/routes/GET_response.json @@ -0,0 +1,88 @@ +{ + "pagination": { + "total_results": 1, + "total_pages": 1, + "first": { + "href": "https://api.example.org/v3/routes?page=1&per_page=2" + }, + "last": { + "href": "https://api.example.org/v3/routes?page=1&per_page=2" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "cbad697f-cac1-48f4-9017-ac08f39dfb31", + "protocol": "http", + "created_at": "2019-05-10T17:17:48Z", + "updated_at": "2019-05-10T17:17:48Z", + "host": "a-hostname", + "path": "/some_path", + "url": "a-hostname.a-domain.com/some_path", + "destinations": [ + { + "guid": "385bf117-17f5-4689-8c5c-08c6cc821fed", + "app": { + "guid": "0a6636b5-7fc4-44d8-8752-0db3e40b35a5", + "process": { + "type": "web" + } + }, + "weight": null, + "port": 8080, + "protocol": "http1", + "created_at": "2019-05-10T17:17:48Z", + "updated_at": "2019-05-10T17:17:48Z" + }, + { + "guid": "27e96a3b-5bcf-49ed-8048-351e0be23e6f", + "app": { + "guid": "f61e59fa-2121-4217-8c7b-15bfd75baf25", + "process": { + "type": "web" + } + }, + "weight": null, + "port": 8080, + "protocol": "http1", + "created_at": "2019-05-10T17:17:48Z", + "updated_at": "2019-05-10T17:17:48Z" + } + ], + "options": { + "loadbalancing": "round-robin" + }, + "metadata": { + "labels": {}, + "annotations": {} + }, + "relationships": { + "space": { + "data": { + "guid": "885a8cb3-c07b-4856-b448-eeb10bf36236" + } + }, + "domain": { + "data": { + "guid": "0b5f3633-194c-42d2-9408-972366617e0e" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/routes/cbad697f-cac1-48f4-9017-ac08f39dfb31" + }, + "space": { + "href": "https://api.example.org/v3/spaces/885a8cb3-c07b-4856-b448-eeb10bf36236" + }, + "domain": { + "href": "https://api.example.org/v3/domains/0b5f3633-194c-42d2-9408-972366617e0e" + }, + "destinations": { + "href": "https://api.example.org/v3/routes/cbad697f-cac1-48f4-9017-ac08f39dfb31/destinations" + } + } + } + ] +} diff --git a/tests/fixtures/v3/routes/GET_{id}_response.json b/tests/fixtures/v3/routes/GET_{id}_response.json new file mode 100644 index 0000000..8284103 --- /dev/null +++ b/tests/fixtures/v3/routes/GET_{id}_response.json @@ -0,0 +1,73 @@ +{ + "guid": "cbad697f-cac1-48f4-9017-ac08f39dfb31", + "protocol": "tcp", + "port": 6666, + "created_at": "2019-05-10T17:17:48Z", + "updated_at": "2019-05-10T17:17:48Z", + "host": "a-hostname", + "path": "/some_path", + "url": "a-hostname.a-domain.com/some_path", + "destinations": [ + { + "guid": "385bf117-17f5-4689-8c5c-08c6cc821fed", + "app": { + "guid": "0a6636b5-7fc4-44d8-8752-0db3e40b35a5", + "process": { + "type": "web" + } + }, + "weight": null, + "port": 8080, + "protocol": "tcp", + "created_at": "2019-05-10T17:17:48Z", + "updated_at": "2019-05-10T17:17:48Z" + }, + { + "guid": "27e96a3b-5bcf-49ed-8048-351e0be23e6f", + "app": { + "guid": "f61e59fa-2121-4217-8c7b-15bfd75baf25", + "process": { + "type": "web" + } + }, + "weight": null, + "port": 8080, + "protocol": "tcp", + "created_at": "2019-05-10T17:17:48Z", + "updated_at": "2019-05-10T17:17:48Z" + } + ], + "options": { + "loadbalancing": "round-robin" + }, + "metadata": { + "labels": {}, + "annotations": {} + }, + "relationships": { + "space": { + "data": { + "guid": "885a8cb3-c07b-4856-b448-eeb10bf36236" + } + }, + "domain": { + "data": { + "guid": "0b5f3633-194c-42d2-9408-972366617e0e" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/routes/cbad697f-cac1-48f4-9017-ac08f39dfb31" + }, + "space": { + "href": "https://api.example.org/v3/spaces/885a8cb3-c07b-4856-b448-eeb10bf36236" + }, + "domain": { + "href": "https://api.example.org/v3/domains/0b5f3633-194c-42d2-9408-972366617e0e" + }, + "destinations": { + "href": "https://api.example.org/v3/routes/cbad697f-cac1-48f4-9017-ac08f39dfb31/destinations" + } + } +} \ No newline at end of file diff --git a/tests/fixtures/v3/routes/PATCH_{id}_response.json b/tests/fixtures/v3/routes/PATCH_{id}_response.json new file mode 100644 index 0000000..82d9734 --- /dev/null +++ b/tests/fixtures/v3/routes/PATCH_{id}_response.json @@ -0,0 +1,77 @@ +{ + "guid": "cbad697f-cac1-48f4-9017-ac08f39dfb31", + "protocol": "tcp", + "port": 6666, + "created_at": "2019-05-10T17:17:48Z", + "updated_at": "2019-05-10T17:17:48Z", + "host": "a-hostname", + "path": "/some_path", + "url": "a-hostname.a-domain.com/some_path", + "destinations": [ + { + "guid": "385bf117-17f5-4689-8c5c-08c6cc821fed", + "app": { + "guid": "0a6636b5-7fc4-44d8-8752-0db3e40b35a5", + "process": { + "type": "web" + } + }, + "weight": null, + "port": 8080, + "protocol": "tcp", + "created_at": "2019-05-10T17:17:48Z", + "updated_at": "2019-05-10T17:17:48Z" + }, + { + "guid": "27e96a3b-5bcf-49ed-8048-351e0be23e6f", + "app": { + "guid": "f61e59fa-2121-4217-8c7b-15bfd75baf25", + "process": { + "type": "web" + } + }, + "weight": null, + "port": 8080, + "protocol": "tcp", + "created_at": "2019-05-10T17:17:48Z", + "updated_at": "2019-05-10T17:17:48Z" + } + ], + "options": { + "loadbalancing": "round-robin" + }, + "metadata": { + "labels": { + "key": "value" + }, + "annotations": { + "note": "detailed information" + } + }, + "relationships": { + "space": { + "data": { + "guid": "885a8cb3-c07b-4856-b448-eeb10bf36236" + } + }, + "domain": { + "data": { + "guid": "0b5f3633-194c-42d2-9408-972366617e0e" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/routes/cbad697f-cac1-48f4-9017-ac08f39dfb31" + }, + "space": { + "href": "https://api.example.org/v3/spaces/885a8cb3-c07b-4856-b448-eeb10bf36236" + }, + "domain": { + "href": "https://api.example.org/v3/domains/0b5f3633-194c-42d2-9408-972366617e0e" + }, + "destinations": { + "href": "https://api.example.org/v3/routes/cbad697f-cac1-48f4-9017-ac08f39dfb31/destinations" + } + } +} \ No newline at end of file diff --git a/tests/fixtures/v3/routes/POST_response.json b/tests/fixtures/v3/routes/POST_response.json new file mode 100644 index 0000000..ec6f099 --- /dev/null +++ b/tests/fixtures/v3/routes/POST_response.json @@ -0,0 +1,73 @@ +{ + "guid": "cbad697f-cac1-48f4-9017-ac08f39dfb31", + "protocol": "tcp", + "port": 6666, + "created_at": "2019-05-10T17:17:48Z", + "updated_at": "2019-05-10T17:17:48Z", + "host": "a-hostname", + "path": "/some_path", + "url": "a-hostname.a-domain.com/some_path", + "destinations": [ + { + "guid": "385bf117-17f5-4689-8c5c-08c6cc821fed", + "app": { + "guid": "0a6636b5-7fc4-44d8-8752-0db3e40b35a5", + "process": { + "type": "web" + } + }, + "weight": null, + "port": 8080, + "protocol": "tcp", + "created_at": "2019-05-10T17:17:48Z", + "updated_at": "2019-05-10T17:17:48Z" + }, + { + "guid": "27e96a3b-5bcf-49ed-8048-351e0be23e6f", + "app": { + "guid": "f61e59fa-2121-4217-8c7b-15bfd75baf25", + "process": { + "type": "web" + } + }, + "weight": null, + "port": 8080, + "protocol": "tcp", + "created_at": "2019-05-10T17:17:48Z", + "updated_at": "2019-05-10T17:17:48Z" + } + ], + "options": { + "loadbalancing": "round-robin" + }, + "metadata": { + "labels": {"key":"value"}, + "annotations": {"note":"detailed information"} + }, + "relationships": { + "space": { + "data": { + "guid": "885a8cb3-c07b-4856-b448-eeb10bf36236" + } + }, + "domain": { + "data": { + "guid": "0b5f3633-194c-42d2-9408-972366617e0e" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/routes/cbad697f-cac1-48f4-9017-ac08f39dfb31" + }, + "space": { + "href": "https://api.example.org/v3/spaces/885a8cb3-c07b-4856-b448-eeb10bf36236" + }, + "domain": { + "href": "https://api.example.org/v3/domains/0b5f3633-194c-42d2-9408-972366617e0e" + }, + "destinations": { + "href": "https://api.example.org/v3/routes/cbad697f-cac1-48f4-9017-ac08f39dfb31/destinations" + } + } +} \ No newline at end of file diff --git a/tests/v3/test_routes.py b/tests/v3/test_routes.py new file mode 100644 index 0000000..1f9c24d --- /dev/null +++ b/tests/v3/test_routes.py @@ -0,0 +1,142 @@ +import unittest +from http import HTTPStatus + +from abstract_test_case import AbstractTestCase +from cloudfoundry_client.v3.entities import Entity +from cloudfoundry_client.v3.routes import LoadBalancing + + +class TestRoutes(unittest.TestCase, AbstractTestCase): + @classmethod + def setUpClass(cls): + cls.mock_client_class() + + def setUp(self): + self.build_client() + + def test_create(self): + self.client.post.return_value = self.mock_response( + "/v3/routes", + HTTPStatus.OK, + None, + "v3", "routes", "POST_response.json" + ) + result = self.client.v3.routes.create( + space_guid="space-guid", + domain_guid="domain-guid", + host="a-hostname", + path="/some_path", + port=6666, + load_balancing=LoadBalancing.ROUND_ROBIN, + meta_labels={"key": "value"}, + meta_annotations={"note": "detailed information"}, + ) + self.client.post.assert_called_with( + self.client.post.return_value.url, + json={ + "host": "a-hostname", + "path": "/some_path", + "port": 6666, + "relationships": { + "domain": { + "data": {"guid": "domain-guid"} + }, + "space": { + "data": {"guid": "space-guid"} + } + }, + "options": { + "loadbalancing": "round-robin" + }, + "metadata": { + "labels": {"key": "value"}, + "annotations": {"note": "detailed information"} + } + }, + files=None, + ) + self.client.post.assert_called_with( + self.client.post.return_value.url, + files=None, + json={ + "host": "a-hostname", + "path": "/some_path", + "port": 6666, + "relationships": { + "domain": { + "data": {"guid": "domain-guid"} + }, + "space": { + "data": {"guid": "space-guid"} + } + }, + "options": { + "loadbalancing": "round-robin" + }, + "metadata": { + "labels": {"key": "value"}, + "annotations": {"note": "detailed information"} + } + }, + ) + self.assertIsNotNone(result) + self.assertIsInstance(result, Entity) + + def test_list(self): + self.client.get.return_value = self.mock_response( + "/v3/routes", + HTTPStatus.OK, + None, + "v3", "routes", "GET_response.json" + ) + all_routes = [route for route in self.client.v3.routes.list()] + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual(1, len(all_routes)) + self.assertEqual(all_routes[0]["protocol"], "http") + for route in all_routes: + self.assertIsInstance(route, Entity) + + def test_get(self): + self.client.get.return_value = self.mock_response( + "/v3/routes/route_id", + HTTPStatus.OK, + None, + "v3", "routes", "GET_{id}_response.json" + ) + result = self.client.v3.routes.get("route_id") + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertIsNotNone(result) + self.assertIsInstance(result, Entity) + + def test_update(self): + self.client.patch.return_value = self.mock_response( + "/v3/routes/route_id", + HTTPStatus.OK, + None, + "v3", "routes", "PATCH_{id}_response.json" + ) + result = self.client.v3.routes.update( + "route_id", + LoadBalancing.LEAST_CONNECTION, + meta_labels={"key": "value"}, + meta_annotations={"note": "detailed information"}, + ) + self.client.patch.assert_called_with( + self.client.patch.return_value.url, + json={ + "options": { + "loadbalancing": "least-connection" + }, + "metadata": { + "labels": {"key": "value"}, + "annotations": {"note": "detailed information"} + } + } + ) + self.assertIsNotNone(result) + self.assertIsInstance(result, Entity) + + def test_remove(self): + self.client.delete.return_value = self.mock_response("/v3/routes/route_id", HTTPStatus.NO_CONTENT, None) + self.client.v3.routes.remove("route_id") + self.client.delete.assert_called_with(self.client.delete.return_value.url) From a9f5c7a43cb85e20898bccf7a7a5b5b7e1be6c2b Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Fri, 9 Jan 2026 22:21:44 +0100 Subject: [PATCH 240/264] feature(v3) add droplets + fixes list app routes + initiate packages (#285) See #280 --- cloudfoundry_client/client.py | 4 + cloudfoundry_client/v3/apps.py | 9 +- cloudfoundry_client/v3/droplets.py | 57 ++++++ cloudfoundry_client/v3/entities.py | 9 +- cloudfoundry_client/v3/packages.py | 16 ++ .../v3/apps/GET_{id}_droplets_response.json | 119 +++++++++++++ .../v3/apps/GET_{id}_routes_response.json | 166 +++++++++--------- tests/fixtures/v3/droplets/GET_response.json | 119 +++++++++++++ .../v3/droplets/GET_{id}_response.json | 59 +++++++ .../v3/droplets/PATCH_{id}_response.json | 63 +++++++ tests/fixtures/v3/droplets/POST_response.json | 57 ++++++ tests/fixtures/v3/packages/GET_response.json | 88 ++++++++++ .../packages/GET_{id}_droplets_response.json | 119 +++++++++++++ .../v3/packages/GET_{id}_response.json | 41 +++++ tests/v3/test_apps.py | 17 +- tests/v3/test_droplets.py | 136 ++++++++++++++ tests/v3/test_packages.py | 48 +++++ tests/v3/test_routes.py | 24 --- 18 files changed, 1040 insertions(+), 111 deletions(-) create mode 100644 cloudfoundry_client/v3/droplets.py create mode 100644 cloudfoundry_client/v3/packages.py create mode 100644 tests/fixtures/v3/apps/GET_{id}_droplets_response.json create mode 100644 tests/fixtures/v3/droplets/GET_response.json create mode 100644 tests/fixtures/v3/droplets/GET_{id}_response.json create mode 100644 tests/fixtures/v3/droplets/PATCH_{id}_response.json create mode 100644 tests/fixtures/v3/droplets/POST_response.json create mode 100644 tests/fixtures/v3/packages/GET_response.json create mode 100644 tests/fixtures/v3/packages/GET_{id}_droplets_response.json create mode 100644 tests/fixtures/v3/packages/GET_{id}_response.json create mode 100644 tests/v3/test_droplets.py create mode 100644 tests/v3/test_packages.py diff --git a/cloudfoundry_client/client.py b/cloudfoundry_client/client.py index 80212ae..98d16f4 100644 --- a/cloudfoundry_client/client.py +++ b/cloudfoundry_client/client.py @@ -30,9 +30,11 @@ from cloudfoundry_client.v3.apps import AppManager from cloudfoundry_client.v3.buildpacks import BuildpackManager from cloudfoundry_client.v3.domains import DomainManager +from cloudfoundry_client.v3.droplets import DropletManager from cloudfoundry_client.v3.feature_flags import FeatureFlagManager from cloudfoundry_client.v3.isolation_segments import IsolationSegmentManager from cloudfoundry_client.v3.organization_quotas import OrganizationQuotaManager +from cloudfoundry_client.v3.packages import PackageManager from cloudfoundry_client.v3.processes import ProcessManager from cloudfoundry_client.v3.organizations import OrganizationManager from cloudfoundry_client.v3.roles import RoleManager @@ -118,11 +120,13 @@ def __init__(self, cloud_controller_v3_url: str, credential_manager: "CloudFound self.apps = AppManager(target_endpoint, credential_manager) self.buildpacks = BuildpackManager(target_endpoint, credential_manager) self.domains = DomainManager(target_endpoint, credential_manager) + self.droplets = DropletManager(target_endpoint, credential_manager) self.feature_flags = FeatureFlagManager(target_endpoint, credential_manager) self.isolation_segments = IsolationSegmentManager(target_endpoint, credential_manager) self.jobs = JobManager(target_endpoint, credential_manager) self.organizations = OrganizationManager(target_endpoint, credential_manager) self.organization_quotas = OrganizationQuotaManager(target_endpoint, credential_manager) + self.packages = PackageManager(target_endpoint, credential_manager) self.processes = ProcessManager(target_endpoint, credential_manager) self.roles = RoleManager(target_endpoint, credential_manager) self.routes = RouteManager(target_endpoint, credential_manager) diff --git a/cloudfoundry_client/v3/apps.py b/cloudfoundry_client/v3/apps.py index 46b9dce..50eb5ca 100644 --- a/cloudfoundry_client/v3/apps.py +++ b/cloudfoundry_client/v3/apps.py @@ -22,8 +22,13 @@ def remove(self, application_guid: str, asynchronous: bool = True) -> str | None def get_env(self, application_guid: str) -> JsonObject: return super(AppManager, self)._get("%s%s/%s/env" % (self.target_endpoint, self.entity_uri, application_guid)) - def get_routes(self, application_guid: str) -> JsonObject: - return super(AppManager, self)._get("%s%s/%s/routes" % (self.target_endpoint, self.entity_uri, application_guid)) + def list_routes(self, application_guid: str, **kwargs) -> Pagination[Entity]: + uri: str = "%s/%s/routes" % (self.entity_uri, application_guid) + return super(AppManager, self)._list(requested_path=uri, **kwargs) + + def list_droplets(self, application_guid: str, **kwargs) -> Pagination[Entity]: + uri: str = "%s/%s/droplets" % (self.entity_uri, application_guid) + return super(AppManager, self)._list(requested_path=uri, **kwargs) def get_manifest(self, application_guid: str) -> str: return self.client.get(url="%s%s/%s/manifest" % (self.target_endpoint, self.entity_uri, application_guid)).text diff --git a/cloudfoundry_client/v3/droplets.py b/cloudfoundry_client/v3/droplets.py new file mode 100644 index 0000000..7f4bb69 --- /dev/null +++ b/cloudfoundry_client/v3/droplets.py @@ -0,0 +1,57 @@ +from typing import TYPE_CHECKING, Any + +from cloudfoundry_client.v3.entities import EntityManager, Entity, ToOneRelationship + +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + + +class DropletManager(EntityManager[Entity]): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(DropletManager, self).__init__(target_endpoint, client, "/v3/droplets") + + def create(self, + app_guid: str, + process_types: dict[str, str] | None = None, + meta_labels: dict | None = None, + meta_annotations: dict | None = None, + ) -> Entity: + data: dict[str, Any] = { + "relationships": { + "app": ToOneRelationship(app_guid) + }, + } + if process_types is not None: + data["process_types"] = process_types + self._metadata(data, meta_labels, meta_annotations) + return super(DropletManager, self)._create(data) + + def copy(self, + droplet_guid: str, + app_guid: str, + meta_labels: dict | None = None, + meta_annotations: dict | None = None, + ) -> Entity: + data: dict[str, Any] = { + "relationships": { + "app": ToOneRelationship(app_guid) + }, + } + self._metadata(data, meta_labels, meta_annotations) + url = EntityManager._get_url_with_encoded_params( + "%s%s" % (self.target_endpoint, self.entity_uri), + source_guid=droplet_guid + ) + return self._post(url, data=data) + + def update(self, + droplet_gid: str, + meta_labels: dict | None = None, + meta_annotations: dict | None = None, + ) -> Entity: + data: dict[str, Any] = {} + self._metadata(data, meta_labels, meta_annotations) + return super(DropletManager, self)._update(droplet_gid, data) + + def remove(self, route_gid: str): + return super(DropletManager, self)._remove(route_gid) diff --git a/cloudfoundry_client/v3/entities.py b/cloudfoundry_client/v3/entities.py index 8a7ba6f..e7924ac 100644 --- a/cloudfoundry_client/v3/entities.py +++ b/cloudfoundry_client/v3/entities.py @@ -135,11 +135,16 @@ def _get(self, url: str, entity_type: type[ENTITY_TYPE] | None = None, **kwargs) def _post( self, url: str, - data: dict | None = None, entity_type: type[ENTITY_TYPE] | None = None, + data: dict | None = None, + params: dict | None = None, files: Any = None ) -> ENTITY_TYPE: - response = self.client.post(url, json=data, files=files) + response = self.client.post( + url if params is None else EntityManager._get_url_with_encoded_params(url, **params), + json=data, + files=files + ) return self._read_response(response, entity_type) def _put(self, url: str, data: dict, entity_type: type[ENTITY_TYPE] | None = None) -> ENTITY_TYPE: diff --git a/cloudfoundry_client/v3/packages.py b/cloudfoundry_client/v3/packages.py new file mode 100644 index 0000000..9e8e3d3 --- /dev/null +++ b/cloudfoundry_client/v3/packages.py @@ -0,0 +1,16 @@ +from typing import TYPE_CHECKING + +from cloudfoundry_client.common_objects import Pagination +from cloudfoundry_client.v3.entities import EntityManager, Entity + +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + + +class PackageManager(EntityManager[Entity]): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(PackageManager, self).__init__(target_endpoint, client, "/v3/packages") + + def list_droplets(self, package_guid: str, **kwargs) -> Pagination[Entity]: + uri: str = "%s/%s/droplets" % (self.entity_uri, package_guid) + return super(PackageManager, self)._list(requested_path=uri, **kwargs) diff --git a/tests/fixtures/v3/apps/GET_{id}_droplets_response.json b/tests/fixtures/v3/apps/GET_{id}_droplets_response.json new file mode 100644 index 0000000..578a68b --- /dev/null +++ b/tests/fixtures/v3/apps/GET_{id}_droplets_response.json @@ -0,0 +1,119 @@ +{ + "pagination": { + "total_results": 2, + "total_pages": 1, + "first": { + "href": "https://api.example.org/v3/app/7b34f1cf-7e73-428a-bb5a-8a17a8058396/droplets?page=1&per_page=50" + }, + "last": { + "href": "https://api.example.org/v3/app/7b34f1cf-7e73-428a-bb5a-8a17a8058396/droplets?page=1&per_page=50" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "585bc3c1-3743-497d-88b0-403ad6b56d16", + "state": "STAGED", + "error": null, + "lifecycle": { + "type": "buildpack", + "data": {} + }, + "image": null, + "execution_metadata": "PRIVATE DATA HIDDEN", + "process_types": { + "redacted_message": "[PRIVATE DATA HIDDEN IN LISTS]" + }, + "checksum": { + "type": "sha256", + "value": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "buildpacks": [ + { + "name": "ruby_buildpack", + "detect_output": "ruby 1.6.14", + "version": "1.1.1.", + "buildpack_name": "ruby" + } + ], + "stack": "cflinuxfs4", + "created_at": "2016-03-28T23:39:34Z", + "updated_at": "2016-03-28T23:39:47Z", + "relationships": { + "app": { + "data": { + "guid": "7b34f1cf-7e73-428a-bb5a-8a17a8058396" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/droplets/585bc3c1-3743-497d-88b0-403ad6b56d16" + }, + "package": { + "href": "https://api.example.org/v3/packages/8222f76a-9e09-4360-b3aa-1ed329945e92" + }, + "app": { + "href": "https://api.example.org/v3/apps/7b34f1cf-7e73-428a-bb5a-8a17a8058396" + }, + "assign_current_droplet": { + "href": "https://api.example.org/v3/apps/7b34f1cf-7e73-428a-bb5a-8a17a8058396/relationships/current_droplet", + "method": "PATCH" + }, + "download": { + "href": "https://api.example.org/v3/droplets/585bc3c1-3743-497d-88b0-403ad6b56d16/download" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } + }, + { + "guid": "fdf3851c-def8-4de1-87f1-6d4543189e22", + "state": "STAGED", + "error": null, + "lifecycle": { + "type": "docker", + "data": {} + }, + "execution_metadata": "[PRIVATE DATA HIDDEN IN LISTS]", + "process_types": { + "redacted_message": "[PRIVATE DATA HIDDEN IN LISTS]" + }, + "image": "cloudfoundry/diego-docker-app-custom:latest", + "checksum": null, + "buildpacks": null, + "stack": null, + "created_at": "2016-03-17T00:00:01Z", + "updated_at": "2016-03-17T21:41:32Z", + "relationships": { + "app": { + "data": { + "guid": "7b34f1cf-7e73-428a-bb5a-8a17a8058396" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/droplets/fdf3851c-def8-4de1-87f1-6d4543189e22" + }, + "package": { + "href": "https://api.example.org/v3/packages/c5725684-a02f-4e59-bc67-8f36ae944688" + }, + "app": { + "href": "https://api.example.org/v3/apps/7b34f1cf-7e73-428a-bb5a-8a17a8058396" + }, + "assign_current_droplet": { + "href": "https://api.example.org/v3/apps/7b34f1cf-7e73-428a-bb5a-8a17a8058396/relationships/current_droplet", + "method": "PATCH" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } + } + ] +} \ No newline at end of file diff --git a/tests/fixtures/v3/apps/GET_{id}_routes_response.json b/tests/fixtures/v3/apps/GET_{id}_routes_response.json index 5a54cf0..9f8d95a 100644 --- a/tests/fixtures/v3/apps/GET_{id}_routes_response.json +++ b/tests/fixtures/v3/apps/GET_{id}_routes_response.json @@ -1,80 +1,88 @@ { - "pagination": { - "total_results": 3, - "total_pages": 2, - "first": { - "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/routes?page=1&per_page=2" - }, - "last": { - "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/routes?page=2&per_page=2" - }, - "next": { - "href": "http://somewhere.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/routes?page=2&per_page=2" - }, - "previous": null - }, - "resources": [ - { - "guid": "cbad697f-cac1-48f4-9017-ac08f39dfb31", - "created_at": "2019-05-10T17:17:48Z", - "updated_at": "2019-05-10T17:17:48Z", - "host": "a-hostname", - "path": "/some_path", - "url": "a-hostname.a-domain.com/some_path", - "destinations": [ - { - "guid": "385bf117-17f5-4689-8c5c-08c6cc821fed", - "app": { - "guid": "0a6636b5-7fc4-44d8-8752-0db3e40b35a5", - "process": { - "type": "web" - } - }, - "weight": null, - "port": 8080 - }, - { - "guid": "27e96a3b-5bcf-49ed-8048-351e0be23e6f", - "app": { - "guid": "f61e59fa-2121-4217-8c7b-15bfd75baf25", - "process": { - "type": "web" - } - }, - "weight": null, - "port": 8080 - } - ], - "metadata": { - "labels": {}, - "annotations": {} - }, - "relationships": { - "space": { - "data": { - "guid": "885a8cb3-c07b-4856-b448-eeb10bf36236" - } - }, - "domain": { - "data": { - "guid": "0b5f3633-194c-42d2-9408-972366617e0e" - } - } - }, - "links": { - "self": { - "href": "http://somewhere.org/v3/routes/cbad697f-cac1-48f4-9017-ac08f39dfb31" - }, - "space": { - "href": "http://somewhere.org/v3/spaces/885a8cb3-c07b-4856-b448-eeb10bf36236" - }, - "domain": { - "href": "http://somewhere.org/v3/domains/0b5f3633-194c-42d2-9408-972366617e0e" - }, - "destinations": { - "href": "http://somewhere.org/v3/routes/cbad697f-cac1-48f4-9017-ac08f39dfb31/destinations" - } - } - } - ] -} + "pagination": { + "total_results": 3, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/routes?page=1&per_page=2" + }, + "last": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/routes?page=2&per_page=2" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "cbad697f-cac1-48f4-9017-ac08f39dfb31", + "protocol": "http", + "created_at": "2019-05-10T17:17:48Z", + "updated_at": "2019-05-10T17:17:48Z", + "host": "a-hostname", + "path": "/some_path", + "url": "a-hostname.a-domain.com/some_path", + "destinations": [ + { + "guid": "385bf117-17f5-4689-8c5c-08c6cc821fed", + "app": { + "guid": "0a6636b5-7fc4-44d8-8752-0db3e40b35a5", + "process": { + "type": "web" + } + }, + "weight": null, + "port": 8080, + "protocol": "http1", + "created_at": "2019-05-10T17:17:48Z", + "updated_at": "2019-05-10T17:17:48Z" + }, + { + "guid": "27e96a3b-5bcf-49ed-8048-351e0be23e6f", + "app": { + "guid": "f61e59fa-2121-4217-8c7b-15bfd75baf25", + "process": { + "type": "web" + } + }, + "weight": null, + "port": 8080, + "protocol": "http1", + "created_at": "2019-05-10T17:17:48Z", + "updated_at": "2019-05-10T17:17:48Z" + } + ], + "options": { + "loadbalancing": "round-robin" + }, + "metadata": { + "labels": {}, + "annotations": {} + }, + "relationships": { + "space": { + "data": { + "guid": "885a8cb3-c07b-4856-b448-eeb10bf36236" + } + }, + "domain": { + "data": { + "guid": "0b5f3633-194c-42d2-9408-972366617e0e" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/routes/cbad697f-cac1-48f4-9017-ac08f39dfb31" + }, + "space": { + "href": "https://api.example.org/v3/spaces/885a8cb3-c07b-4856-b448-eeb10bf36236" + }, + "domain": { + "href": "https://api.example.org/v3/domains/0b5f3633-194c-42d2-9408-972366617e0e" + }, + "destinations": { + "href": "https://api.example.org/v3/routes/cbad697f-cac1-48f4-9017-ac08f39dfb31/destinations" + } + } + } + ] +} \ No newline at end of file diff --git a/tests/fixtures/v3/droplets/GET_response.json b/tests/fixtures/v3/droplets/GET_response.json new file mode 100644 index 0000000..4d3121f --- /dev/null +++ b/tests/fixtures/v3/droplets/GET_response.json @@ -0,0 +1,119 @@ +{ + "pagination": { + "total_results": 2, + "total_pages": 1, + "first": { + "href": "https://api.example.org/v3/droplets?page=1&per_page=50" + }, + "last": { + "href": "https://api.example.org/v3/droplets?page=1&per_page=50" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "585bc3c1-3743-497d-88b0-403ad6b56d16", + "state": "STAGED", + "error": null, + "lifecycle": { + "type": "buildpack", + "data": {} + }, + "image": null, + "execution_metadata": "PRIVATE DATA HIDDEN", + "process_types": { + "redacted_message": "[PRIVATE DATA HIDDEN IN LISTS]" + }, + "checksum": { + "type": "sha256", + "value": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "buildpacks": [ + { + "name": "ruby_buildpack", + "detect_output": "ruby 1.6.14", + "version": "1.1.1.", + "buildpack_name": "ruby" + } + ], + "stack": "cflinuxfs4", + "created_at": "2016-03-28T23:39:34Z", + "updated_at": "2016-03-28T23:39:47Z", + "relationships": { + "app": { + "data": { + "guid": "7b34f1cf-7e73-428a-bb5a-8a17a8058396" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/droplets/585bc3c1-3743-497d-88b0-403ad6b56d16" + }, + "package": { + "href": "https://api.example.org/v3/packages/8222f76a-9e09-4360-b3aa-1ed329945e92" + }, + "app": { + "href": "https://api.example.org/v3/apps/7b34f1cf-7e73-428a-bb5a-8a17a8058396" + }, + "assign_current_droplet": { + "href": "https://api.example.org/v3/apps/7b34f1cf-7e73-428a-bb5a-8a17a8058396/relationships/current_droplet", + "method": "PATCH" + }, + "download": { + "href": "https://api.example.org/v3/droplets/585bc3c1-3743-497d-88b0-403ad6b56d16/download" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } + }, + { + "guid": "fdf3851c-def8-4de1-87f1-6d4543189e22", + "state": "STAGED", + "error": null, + "lifecycle": { + "type": "docker", + "data": {} + }, + "execution_metadata": "[PRIVATE DATA HIDDEN IN LISTS]", + "process_types": { + "redacted_message": "[PRIVATE DATA HIDDEN IN LISTS]" + }, + "image": "cloudfoundry/diego-docker-app-custom:latest", + "checksum": null, + "buildpacks": null, + "stack": null, + "created_at": "2016-03-17T00:00:01Z", + "updated_at": "2016-03-17T21:41:32Z", + "relationships": { + "app": { + "data": { + "guid": "7b34f1cf-7e73-428a-bb5a-8a17a8058396" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/droplets/fdf3851c-def8-4de1-87f1-6d4543189e22" + }, + "package": { + "href": "https://api.example.org/v3/packages/c5725684-a02f-4e59-bc67-8f36ae944688" + }, + "app": { + "href": "https://api.example.org/v3/apps/7b34f1cf-7e73-428a-bb5a-8a17a8058396" + }, + "assign_current_droplet": { + "href": "https://api.example.org/v3/apps/7b34f1cf-7e73-428a-bb5a-8a17a8058396/relationships/current_droplet", + "method": "PATCH" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } + } + ] +} diff --git a/tests/fixtures/v3/droplets/GET_{id}_response.json b/tests/fixtures/v3/droplets/GET_{id}_response.json new file mode 100644 index 0000000..8c2ab9c --- /dev/null +++ b/tests/fixtures/v3/droplets/GET_{id}_response.json @@ -0,0 +1,59 @@ +{ + "guid": "585bc3c1-3743-497d-88b0-403ad6b56d16", + "state": "STAGED", + "error": null, + "lifecycle": { + "type": "buildpack", + "data": {} + }, + "execution_metadata": "", + "process_types": { + "rake": "bundle exec rake", + "web": "bundle exec rackup config.ru -p $PORT" + }, + "checksum": { + "type": "sha256", + "value": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "buildpacks": [ + { + "name": "ruby_buildpack", + "detect_output": "ruby 1.6.14", + "version": "1.1.1.", + "buildpack_name": "ruby" + } + ], + "stack": "cflinuxfs4", + "image": null, + "created_at": "2016-03-28T23:39:34Z", + "updated_at": "2016-03-28T23:39:47Z", + "relationships": { + "app": { + "data": { + "guid": "7b34f1cf-7e73-428a-bb5a-8a17a8058396" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/droplets/585bc3c1-3743-497d-88b0-403ad6b56d16" + }, + "package": { + "href": "https://api.example.org/v3/packages/8222f76a-9e09-4360-b3aa-1ed329945e92" + }, + "app": { + "href": "https://api.example.org/v3/apps/7b34f1cf-7e73-428a-bb5a-8a17a8058396" + }, + "assign_current_droplet": { + "href": "https://api.example.org/v3/apps/7b34f1cf-7e73-428a-bb5a-8a17a8058396/relationships/current_droplet", + "method": "PATCH" + }, + "download": { + "href": "https://api.example.org/v3/droplets/585bc3c1-3743-497d-88b0-403ad6b56d16/download" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } +} \ No newline at end of file diff --git a/tests/fixtures/v3/droplets/PATCH_{id}_response.json b/tests/fixtures/v3/droplets/PATCH_{id}_response.json new file mode 100644 index 0000000..e3d6353 --- /dev/null +++ b/tests/fixtures/v3/droplets/PATCH_{id}_response.json @@ -0,0 +1,63 @@ +{ + "guid": "585bc3c1-3743-497d-88b0-403ad6b56d16", + "state": "STAGED", + "error": null, + "lifecycle": { + "type": "buildpack", + "data": {} + }, + "execution_metadata": "", + "process_types": { + "rake": "bundle exec rake", + "web": "bundle exec rackup config.ru -p $PORT" + }, + "checksum": { + "type": "sha256", + "value": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "buildpacks": [ + { + "name": "ruby_buildpack", + "detect_output": "ruby 1.6.14", + "version": "1.1.1.", + "buildpack_name": "ruby" + } + ], + "stack": "cflinuxfs4", + "image": null, + "created_at": "2016-03-28T23:39:34Z", + "updated_at": "2016-03-28T23:39:47Z", + "relationships": { + "app": { + "data": { + "guid": "7b34f1cf-7e73-428a-bb5a-8a17a8058396" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/droplets/585bc3c1-3743-497d-88b0-403ad6b56d16" + }, + "package": { + "href": "https://api.example.org/v3/packages/8222f76a-9e09-4360-b3aa-1ed329945e92" + }, + "app": { + "href": "https://api.example.org/v3/apps/7b34f1cf-7e73-428a-bb5a-8a17a8058396" + }, + "assign_current_droplet": { + "href": "https://api.example.org/v3/apps/7b34f1cf-7e73-428a-bb5a-8a17a8058396/relationships/current_droplet", + "method": "PATCH" + }, + "download": { + "href": "https://api.example.org/v3/droplets/585bc3c1-3743-497d-88b0-403ad6b56d16/download" + } + }, + "metadata": { + "labels": { + "release": "stable" + }, + "annotations": { + "note": "detailed information" + } + } + } \ No newline at end of file diff --git a/tests/fixtures/v3/droplets/POST_response.json b/tests/fixtures/v3/droplets/POST_response.json new file mode 100644 index 0000000..9707e9b --- /dev/null +++ b/tests/fixtures/v3/droplets/POST_response.json @@ -0,0 +1,57 @@ +{ + "guid": "585bc3c1-3743-497d-88b0-403ad6b56d16", + "state": "AWAITING_UPLOAD", + "error": null, + "lifecycle": { + "type": "buildpack", + "data": {} + }, + "execution_metadata": "", + "process_types": { + "rake": "bundle exec rake", + "web": "bundle exec rackup config.ru -p $PORT" + }, + "checksum": { + "type": "sha256", + "value": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "buildpacks": [ + { + "name": "ruby_buildpack", + "detect_output": "ruby 1.6.14", + "version": "1.1.1.", + "buildpack_name": "ruby" + } + ], + "stack": "cflinuxfs4", + "image": null, + "created_at": "2016-03-28T23:39:34Z", + "updated_at": "2016-03-28T23:39:47Z", + "relationships": { + "app": { + "data": { + "guid": "7b34f1cf-7e73-428a-bb5a-8a17a8058396" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/droplets/585bc3c1-3743-497d-88b0-403ad6b56d16" + }, + "app": { + "href": "https://api.example.org/v3/apps/7b34f1cf-7e73-428a-bb5a-8a17a8058396" + }, + "assign_current_droplet": { + "href": "https://api.example.org/v3/apps/7b34f1cf-7e73-428a-bb5a-8a17a8058396/relationships/current_droplet", + "method": "PATCH" + }, + "upload": { + "href": "https://api.example.org/v3/droplets/585bc3c1-3743-497d-88b0-403ad6b56d16/upload", + "method": "POST" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } +} \ No newline at end of file diff --git a/tests/fixtures/v3/packages/GET_response.json b/tests/fixtures/v3/packages/GET_response.json new file mode 100644 index 0000000..89964d7 --- /dev/null +++ b/tests/fixtures/v3/packages/GET_response.json @@ -0,0 +1,88 @@ +{ + "pagination": { + "total_results": 2, + "total_pages": 1, + "first": { + "href": "https://api.example.org/v3/packages?types=bits%2Cdocker&page=1&per_page=2" + }, + "last": { + "href": "https://api.example.org/v3/packages?types=bits%2Cdocker&page=1&per_page=2" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "a57fd932-85db-483a-a27e-b00efbb3b0a4", + "type": "bits", + "data": { + "checksum": { + "type": "sha256", + "value": null + }, + "error": null + }, + "state": "AWAITING_UPLOAD", + "created_at": "2015-11-03T00:53:54Z", + "updated_at": "2016-06-08T16:41:26Z", + "relationships": { + "app": { + "data": { + "guid": "fa3558ce-1c4d-46fc-9776-54b9c8021745" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/packages/a57fd932-85db-483a-a27e-b00efbb3b0a4" + }, + "upload": { + "href": "https://api.example.org/v3/packages/a57fd932-85db-483a-a27e-b00efbb3b0a4/upload", + "method": "POST" + }, + "download": { + "href": "https://api.example.org/v3/packages/a57fd932-85db-483a-a27e-b00efbb3b0a4/download", + "method": "GET" + }, + "app": { + "href": "https://api.example.org/v3/apps/fa3558ce-1c4d-46fc-9776-54b9c8021745" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } + }, + { + "guid": "8f1f294d-cef8-4c11-9f0b-3bcdc0bd2691", + "type": "docker", + "data": { + "image": "registry/image:latest", + "username": "username", + "password": "***" + }, + "state": "READY", + "created_at": "2015-11-03T00:53:54Z", + "updated_at": "2016-06-08T16:41:26Z", + "relationships": { + "app": { + "data": { + "guid": "fa3558ce-1c4d-46fc-9776-54b9c8021745" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/packages/8f1f294d-cef8-4c11-9f0b-3bcdc0bd2691" + }, + "app": { + "href": "https://api.example.org/v3/apps/fa3558ce-1c4d-46fc-9776-54b9c8021745" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } + } + ] +} \ No newline at end of file diff --git a/tests/fixtures/v3/packages/GET_{id}_droplets_response.json b/tests/fixtures/v3/packages/GET_{id}_droplets_response.json new file mode 100644 index 0000000..b8d2c83 --- /dev/null +++ b/tests/fixtures/v3/packages/GET_{id}_droplets_response.json @@ -0,0 +1,119 @@ +{ + "pagination": { + "total_results": 2, + "total_pages": 1, + "first": { + "href": "https://api.example.org/v3/packages/7b34f1cf-7e73-428a-bb5a-8a17a8058396/droplets?page=1&per_page=50" + }, + "last": { + "href": "https://api.example.org/v3/packages/7b34f1cf-7e73-428a-bb5a-8a17a8058396/droplets?page=1&per_page=50" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "585bc3c1-3743-497d-88b0-403ad6b56d16", + "state": "STAGED", + "error": null, + "lifecycle": { + "type": "buildpack", + "data": {} + }, + "image": null, + "execution_metadata": "PRIVATE DATA HIDDEN", + "process_types": { + "redacted_message": "[PRIVATE DATA HIDDEN IN LISTS]" + }, + "checksum": { + "type": "sha256", + "value": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "buildpacks": [ + { + "name": "ruby_buildpack", + "detect_output": "ruby 1.6.14", + "version": "1.1.1.", + "buildpack_name": "ruby" + } + ], + "stack": "cflinuxfs4", + "created_at": "2016-03-28T23:39:34Z", + "updated_at": "2016-03-28T23:39:47Z", + "relationships": { + "app": { + "data": { + "guid": "7b34f1cf-7e73-428a-bb5a-8a17a8058396" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/droplets/585bc3c1-3743-497d-88b0-403ad6b56d16" + }, + "package": { + "href": "https://api.example.org/v3/packages/8222f76a-9e09-4360-b3aa-1ed329945e92" + }, + "app": { + "href": "https://api.example.org/v3/apps/7b34f1cf-7e73-428a-bb5a-8a17a8058396" + }, + "assign_current_droplet": { + "href": "https://api.example.org/v3/apps/7b34f1cf-7e73-428a-bb5a-8a17a8058396/relationships/current_droplet", + "method": "PATCH" + }, + "download": { + "href": "https://api.example.org/v3/droplets/585bc3c1-3743-497d-88b0-403ad6b56d16/download" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } + }, + { + "guid": "fdf3851c-def8-4de1-87f1-6d4543189e22", + "state": "STAGED", + "error": null, + "lifecycle": { + "type": "docker", + "data": {} + }, + "execution_metadata": "[PRIVATE DATA HIDDEN IN LISTS]", + "process_types": { + "redacted_message": "[PRIVATE DATA HIDDEN IN LISTS]" + }, + "image": "cloudfoundry/diego-docker-app-custom:latest", + "checksum": null, + "buildpacks": null, + "stack": null, + "created_at": "2016-03-17T00:00:01Z", + "updated_at": "2016-03-17T21:41:32Z", + "relationships": { + "app": { + "data": { + "guid": "7b34f1cf-7e73-428a-bb5a-8a17a8058396" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/droplets/fdf3851c-def8-4de1-87f1-6d4543189e22" + }, + "package": { + "href": "https://api.example.org/v3/packages/c5725684-a02f-4e59-bc67-8f36ae944688" + }, + "app": { + "href": "https://api.example.org/v3/apps/7b34f1cf-7e73-428a-bb5a-8a17a8058396" + }, + "assign_current_droplet": { + "href": "https://api.example.org/v3/apps/7b34f1cf-7e73-428a-bb5a-8a17a8058396/relationships/current_droplet", + "method": "PATCH" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } + } + ] +} \ No newline at end of file diff --git a/tests/fixtures/v3/packages/GET_{id}_response.json b/tests/fixtures/v3/packages/GET_{id}_response.json new file mode 100644 index 0000000..3e72e01 --- /dev/null +++ b/tests/fixtures/v3/packages/GET_{id}_response.json @@ -0,0 +1,41 @@ +{ + "guid": "44f7c078-0934-470f-9883-4fcddc5b8f13", + "type": "bits", + "data": { + "checksum": { + "type": "sha256", + "value": null + }, + "error": null + }, + "state": "PROCESSING_UPLOAD", + "created_at": "2015-11-13T17:02:56Z", + "updated_at": "2016-06-08T16:41:26Z", + "relationships": { + "app": { + "data": { + "guid": "1d3bf0ec-5806-43c4-b64e-8364dba1086a" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/packages/44f7c078-0934-470f-9883-4fcddc5b8f13" + }, + "upload": { + "href": "https://api.example.org/v3/packages/44f7c078-0934-470f-9883-4fcddc5b8f13/upload", + "method": "POST" + }, + "download": { + "href": "https://api.example.org/v3/packages/44f7c078-0934-470f-9883-4fcddc5b8f13/download", + "method": "GET" + }, + "app": { + "href": "https://api.example.org/v3/apps/1d3bf0ec-5806-43c4-b64e-8364dba1086a" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } +} diff --git a/tests/v3/test_apps.py b/tests/v3/test_apps.py index 6039f1b..67d0827 100644 --- a/tests/v3/test_apps.py +++ b/tests/v3/test_apps.py @@ -97,13 +97,22 @@ def test_get_env(self): self.assertIsInstance(env, JsonObject) self.assertEqual(env["application_env_json"]["VCAP_APPLICATION"]["limits"]["fds"], 16384) - def test_get_routes(self): + def test_list_routes(self): self.client.get.return_value = self.mock_response( "/v3/apps/app_id/routes", HTTPStatus.OK, None, "v3", "apps", "GET_{id}_routes_response.json" ) - routes = self.client.v3.apps.get_routes("app_id") - self.assertIsInstance(routes, JsonObject) - self.assertEqual(routes["resources"][0]["destinations"][0]["guid"], "385bf117-17f5-4689-8c5c-08c6cc821fed") + routes: list[dict] = [revision for revision in self.client.v3.apps.list_routes("app_id")] + self.assertEqual(1, len(routes)) + + self.assertEqual(routes[0]["destinations"][0]["guid"], "385bf117-17f5-4689-8c5c-08c6cc821fed") + + def test_list_droplets(self): + self.client.get.return_value = self.mock_response( + "/v3/apps/app_id/droplets", HTTPStatus.OK, None, "v3", "apps", "GET_{id}_droplets_response.json" + ) + droplets: list[dict] = [revision for revision in self.client.v3.apps.list_droplets("app_id")] + self.assertEqual(2, len(droplets)) + self.assertEqual(droplets[0]["state"], "STAGED") def test_get_include_space_and_org(self): self.client.get.return_value = self.mock_response( diff --git a/tests/v3/test_droplets.py b/tests/v3/test_droplets.py new file mode 100644 index 0000000..ebfd2be --- /dev/null +++ b/tests/v3/test_droplets.py @@ -0,0 +1,136 @@ +import unittest +from http import HTTPStatus + +from abstract_test_case import AbstractTestCase +from cloudfoundry_client.v3.entities import Entity + + +class TestDroplets(unittest.TestCase, AbstractTestCase): + @classmethod + def setUpClass(cls): + cls.mock_client_class() + + def setUp(self): + self.build_client() + + def test_create(self): + self.client.post.return_value = self.mock_response( + "/v3/droplets", + HTTPStatus.OK, + None, + "v3", "droplets", "POST_response.json" + ) + result = self.client.v3.droplets.create( + app_guid="app-guid", + process_types={ + "rake": "bundle exec rake", + "web": "bundle exec rackup config.ru -p $PORT" + }, + meta_labels={"key": "value"}, + meta_annotations={"note": "detailed information"}, + ) + self.client.post.assert_called_with( + self.client.post.return_value.url, + json={ + "relationships": { + "app": { + "data": { + "guid": "app-guid" + } + } + }, + "process_types": { + "rake": "bundle exec rake", + "web": "bundle exec rackup config.ru -p $PORT" + }, + "metadata": {"labels": {"key": "value"}, "annotations": {"note": "detailed information"}} + }, + files=None, + ) + self.assertIsNotNone(result) + self.assertIsInstance(result, Entity) + + def test_copy(self): + self.client.post.return_value = self.mock_response( + "/v3/droplets?source_guid=droplet_id", + HTTPStatus.OK, + None, + "v3", "droplets", "POST_response.json" + ) + result = self.client.v3.droplets.copy( + droplet_guid="droplet_id", + app_guid="app-guid", + meta_labels={"key": "value"}, + meta_annotations={"note": "detailed information"}, + ) + self.client.post.assert_called_with( + self.client.post.return_value.url, + json={ + "relationships": { + "app": { + "data": { + "guid": "app-guid" + } + } + }, + "metadata": {"labels": {"key": "value"}, "annotations": {"note": "detailed information"}} + }, + files=None, + ) + self.assertIsNotNone(result) + self.assertIsInstance(result, Entity) + + def test_list(self): + self.client.get.return_value = self.mock_response( + "/v3/droplets", + HTTPStatus.OK, + None, + "v3", "droplets", "GET_response.json" + ) + all_droplets = [droplet for droplet in self.client.v3.droplets.list()] + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual(2, len(all_droplets)) + self.assertEqual(all_droplets[0]["state"], "STAGED") + for droplet in all_droplets: + self.assertIsInstance(droplet, Entity) + + def test_get(self): + self.client.get.return_value = self.mock_response( + "/v3/droplets/route_id", + HTTPStatus.OK, + None, + "v3", "droplets", "GET_{id}_response.json" + ) + result = self.client.v3.droplets.get("route_id") + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertIsNotNone(result) + self.assertIsInstance(result, Entity) + + def test_update(self): + self.client.patch.return_value = self.mock_response( + "/v3/droplets/droplet_id", + HTTPStatus.OK, + None, + "v3", "droplets", "PATCH_{id}_response.json" + ) + result = self.client.v3.droplets.update( + "droplet_id", + meta_labels={"key": "value"}, + meta_annotations={"note": "detailed information"}, + ) + self.client.patch.assert_called_with( + self.client.patch.return_value.url, + json={ + "metadata": { + "labels": {"key": "value"}, + "annotations": {"note": "detailed information"} + } + } + ) + self.assertIsNotNone(result) + self.assertIsInstance(result, Entity) + + def test_remove(self): + self.client.delete.return_value = self.mock_response("/v3/droplets/droplet_id", HTTPStatus.NO_CONTENT, None) + self.client.v3.droplets.remove("droplet_id") + self.client.delete.assert_called_with(self.client.delete.return_value.url) diff --git a/tests/v3/test_packages.py b/tests/v3/test_packages.py new file mode 100644 index 0000000..0db7c46 --- /dev/null +++ b/tests/v3/test_packages.py @@ -0,0 +1,48 @@ +import unittest +from http import HTTPStatus + +from abstract_test_case import AbstractTestCase +from cloudfoundry_client.v3.entities import Entity + + +class TestPackages(unittest.TestCase, AbstractTestCase): + @classmethod + def setUpClass(cls): + cls.mock_client_class() + + def setUp(self): + self.build_client() + + def test_list(self): + self.client.get.return_value = self.mock_response( + "/v3/packages", + HTTPStatus.OK, + None, + "v3", "packages", "GET_response.json" + ) + all_packages = [package for package in self.client.v3.packages.list()] + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual(2, len(all_packages)) + self.assertEqual(all_packages[0]["type"], "bits") + for droplet in all_packages: + self.assertIsInstance(droplet, Entity) + + def test_get(self): + self.client.get.return_value = self.mock_response( + "/v3/packages/package_id", + HTTPStatus.OK, + None, + "v3", "packages", "GET_{id}_response.json" + ) + result = self.client.v3.packages.get("package_id") + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertIsNotNone(result) + self.assertIsInstance(result, Entity) + + def test_list_droplets(self): + self.client.get.return_value = self.mock_response( + "/v3/packages/package_id/droplets", HTTPStatus.OK, None, "v3", "packages", "GET_{id}_droplets_response.json" + ) + droplets: list[dict] = [droplet for droplet in self.client.v3.packages.list_droplets("package_id")] + self.assertEqual(2, len(droplets)) + self.assertEqual(droplets[0]["state"], "STAGED") diff --git a/tests/v3/test_routes.py b/tests/v3/test_routes.py index 1f9c24d..4bb60f8 100644 --- a/tests/v3/test_routes.py +++ b/tests/v3/test_routes.py @@ -55,30 +55,6 @@ def test_create(self): }, files=None, ) - self.client.post.assert_called_with( - self.client.post.return_value.url, - files=None, - json={ - "host": "a-hostname", - "path": "/some_path", - "port": 6666, - "relationships": { - "domain": { - "data": {"guid": "domain-guid"} - }, - "space": { - "data": {"guid": "space-guid"} - } - }, - "options": { - "loadbalancing": "round-robin" - }, - "metadata": { - "labels": {"key": "value"}, - "annotations": {"note": "detailed information"} - } - }, - ) self.assertIsNotNone(result) self.assertIsInstance(result, Entity) From 2ca8f60f190c0489b604c5fa18bfc06e96f856f3 Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Fri, 9 Jan 2026 22:40:42 +0100 Subject: [PATCH 241/264] feature(v3) add create / copy operation for packages and list app's packages (#286) See #280 --- cloudfoundry_client/v3/apps.py | 4 ++ cloudfoundry_client/v3/packages.py | 43 +++++++++++- .../v3/apps/GET_{id}_packages_response.json | 57 ++++++++++++++++ tests/fixtures/v3/packages/POST_response.json | 41 ++++++++++++ tests/v3/test_apps.py | 24 ++++--- tests/v3/test_packages.py | 66 ++++++++++++++++++- 6 files changed, 223 insertions(+), 12 deletions(-) create mode 100644 tests/fixtures/v3/apps/GET_{id}_packages_response.json create mode 100644 tests/fixtures/v3/packages/POST_response.json diff --git a/cloudfoundry_client/v3/apps.py b/cloudfoundry_client/v3/apps.py index 50eb5ca..3357164 100644 --- a/cloudfoundry_client/v3/apps.py +++ b/cloudfoundry_client/v3/apps.py @@ -33,6 +33,10 @@ def list_droplets(self, application_guid: str, **kwargs) -> Pagination[Entity]: def get_manifest(self, application_guid: str) -> str: return self.client.get(url="%s%s/%s/manifest" % (self.target_endpoint, self.entity_uri, application_guid)).text + def list_packages(self, application_guid: str, **kwargs) -> Pagination[Entity]: + uri: str = "%s/%s/packages" % (self.entity_uri, application_guid) + return super(AppManager, self)._list(requested_path=uri, **kwargs) + def list_revisions(self, application_guid: str, **kwargs) -> Pagination[Entity]: uri: str = "%s/%s/revisions" % (self.entity_uri, application_guid) return super(AppManager, self)._list(requested_path=uri, **kwargs) diff --git a/cloudfoundry_client/v3/packages.py b/cloudfoundry_client/v3/packages.py index 9e8e3d3..cb554dd 100644 --- a/cloudfoundry_client/v3/packages.py +++ b/cloudfoundry_client/v3/packages.py @@ -1,16 +1,55 @@ -from typing import TYPE_CHECKING +from enum import Enum +from typing import TYPE_CHECKING, Any from cloudfoundry_client.common_objects import Pagination -from cloudfoundry_client.v3.entities import EntityManager, Entity +from cloudfoundry_client.v3.entities import EntityManager, Entity, ToOneRelationship if TYPE_CHECKING: from cloudfoundry_client.client import CloudFoundryClient +class PackageType(Enum): + BITS = 'bits' + DOCKER = 'docker' + + class PackageManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super(PackageManager, self).__init__(target_endpoint, client, "/v3/packages") + def create(self, + app_guid: str, + package_type: PackageType, + meta_labels: dict | None = None, + meta_annotations: dict | None = None, + ) -> Entity: + data: dict[str, Any] = { + "type": package_type.value, + "relationships": { + "app": ToOneRelationship(app_guid) + }, + } + self._metadata(data, meta_labels, meta_annotations) + return super(PackageManager, self)._create(data) + + def copy(self, + package_guid: str, + app_guid: str, + meta_labels: dict | None = None, + meta_annotations: dict | None = None, + ) -> Entity: + data: dict[str, Any] = { + "relationships": { + "app": ToOneRelationship(app_guid) + }, + } + self._metadata(data, meta_labels, meta_annotations) + url = EntityManager._get_url_with_encoded_params( + "%s%s" % (self.target_endpoint, self.entity_uri), + source_guid=package_guid + ) + return self._post(url, data=data) + def list_droplets(self, package_guid: str, **kwargs) -> Pagination[Entity]: uri: str = "%s/%s/droplets" % (self.entity_uri, package_guid) return super(PackageManager, self)._list(requested_path=uri, **kwargs) diff --git a/tests/fixtures/v3/apps/GET_{id}_packages_response.json b/tests/fixtures/v3/apps/GET_{id}_packages_response.json new file mode 100644 index 0000000..8c48599 --- /dev/null +++ b/tests/fixtures/v3/apps/GET_{id}_packages_response.json @@ -0,0 +1,57 @@ +{ + "pagination": { + "total_results": 1, + "total_pages": 1, + "first": { + "href": "https://api.example.org/v3/apps/f2efe391-2b5b-4836-8518-ad93fa9ebf69/packages?states=READY&page=1&per_page=50" + }, + "last": { + "href": "https://api.example.org/v3/apps/f2efe391-2b5b-4836-8518-ad93fa9ebf69/packages?states=READY&page=1&per_page=50" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "752edab0-2147-4f58-9c25-cd72ad8c3561", + "type": "bits", + "data": { + "error": null, + "checksum": { + "type": "sha256", + "value": null + } + }, + "state": "READY", + "created_at": "2016-03-17T21:41:09Z", + "updated_at": "2016-06-08T16:41:26Z", + "relationships": { + "app": { + "data": { + "guid": "f2efe391-2b5b-4836-8518-ad93fa9ebf69" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/packages/752edab0-2147-4f58-9c25-cd72ad8c3561" + }, + "upload": { + "href": "https://api.example.org/v3/packages/752edab0-2147-4f58-9c25-cd72ad8c3561/upload", + "method": "POST" + }, + "download": { + "href": "https://api.example.org/v3/packages/752edab0-2147-4f58-9c25-cd72ad8c3561/download", + "method": "GET" + }, + "app": { + "href": "https://api.example.org/v3/apps/f2efe391-2b5b-4836-8518-ad93fa9ebf69" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } + } + ] +} diff --git a/tests/fixtures/v3/packages/POST_response.json b/tests/fixtures/v3/packages/POST_response.json new file mode 100644 index 0000000..3e72e01 --- /dev/null +++ b/tests/fixtures/v3/packages/POST_response.json @@ -0,0 +1,41 @@ +{ + "guid": "44f7c078-0934-470f-9883-4fcddc5b8f13", + "type": "bits", + "data": { + "checksum": { + "type": "sha256", + "value": null + }, + "error": null + }, + "state": "PROCESSING_UPLOAD", + "created_at": "2015-11-13T17:02:56Z", + "updated_at": "2016-06-08T16:41:26Z", + "relationships": { + "app": { + "data": { + "guid": "1d3bf0ec-5806-43c4-b64e-8364dba1086a" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/packages/44f7c078-0934-470f-9883-4fcddc5b8f13" + }, + "upload": { + "href": "https://api.example.org/v3/packages/44f7c078-0934-470f-9883-4fcddc5b8f13/upload", + "method": "POST" + }, + "download": { + "href": "https://api.example.org/v3/packages/44f7c078-0934-470f-9883-4fcddc5b8f13/download", + "method": "GET" + }, + "app": { + "href": "https://api.example.org/v3/apps/1d3bf0ec-5806-43c4-b64e-8364dba1086a" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } +} diff --git a/tests/v3/test_apps.py b/tests/v3/test_apps.py index 67d0827..dbe8fb6 100644 --- a/tests/v3/test_apps.py +++ b/tests/v3/test_apps.py @@ -97,6 +97,22 @@ def test_get_env(self): self.assertIsInstance(env, JsonObject) self.assertEqual(env["application_env_json"]["VCAP_APPLICATION"]["limits"]["fds"], 16384) + def test_list_droplets(self): + self.client.get.return_value = self.mock_response( + "/v3/apps/app_id/droplets", HTTPStatus.OK, None, "v3", "apps", "GET_{id}_droplets_response.json" + ) + droplets: list[dict] = [revision for revision in self.client.v3.apps.list_droplets("app_id")] + self.assertEqual(2, len(droplets)) + self.assertEqual(droplets[0]["state"], "STAGED") + + def test_list_packages(self): + self.client.get.return_value = self.mock_response( + "/v3/apps/app_id/packages", HTTPStatus.OK, None, "v3", "apps", "GET_{id}_packages_response.json" + ) + packages: list[dict] = [package for package in self.client.v3.apps.list_packages("app_id")] + self.assertEqual(1, len(packages)) + self.assertEqual(packages[0]["type"], "bits") + def test_list_routes(self): self.client.get.return_value = self.mock_response( "/v3/apps/app_id/routes", HTTPStatus.OK, None, "v3", "apps", "GET_{id}_routes_response.json" @@ -106,14 +122,6 @@ def test_list_routes(self): self.assertEqual(routes[0]["destinations"][0]["guid"], "385bf117-17f5-4689-8c5c-08c6cc821fed") - def test_list_droplets(self): - self.client.get.return_value = self.mock_response( - "/v3/apps/app_id/droplets", HTTPStatus.OK, None, "v3", "apps", "GET_{id}_droplets_response.json" - ) - droplets: list[dict] = [revision for revision in self.client.v3.apps.list_droplets("app_id")] - self.assertEqual(2, len(droplets)) - self.assertEqual(droplets[0]["state"], "STAGED") - def test_get_include_space_and_org(self): self.client.get.return_value = self.mock_response( "/v3/apps/app_id?include=space.organization", diff --git a/tests/v3/test_packages.py b/tests/v3/test_packages.py index 0db7c46..e19e13c 100644 --- a/tests/v3/test_packages.py +++ b/tests/v3/test_packages.py @@ -3,6 +3,7 @@ from abstract_test_case import AbstractTestCase from cloudfoundry_client.v3.entities import Entity +from cloudfoundry_client.v3.packages import PackageType class TestPackages(unittest.TestCase, AbstractTestCase): @@ -13,6 +14,67 @@ def setUpClass(cls): def setUp(self): self.build_client() + def test_create(self): + self.client.post.return_value = self.mock_response( + "/v3/packages", + HTTPStatus.OK, + None, + "v3", "packages", "POST_response.json" + ) + result = self.client.v3.packages.create( + app_guid="app-guid", + package_type=PackageType.BITS, + meta_labels={"key": "value"}, + meta_annotations={"note": "detailed information"}, + ) + self.client.post.assert_called_with( + self.client.post.return_value.url, + json={ + "relationships": { + "app": { + "data": { + "guid": "app-guid" + } + } + }, + "type": "bits", + "metadata": {"labels": {"key": "value"}, "annotations": {"note": "detailed information"}} + }, + files=None, + ) + self.assertIsNotNone(result) + self.assertIsInstance(result, Entity) + + def test_copy(self): + self.client.post.return_value = self.mock_response( + "/v3/packages?source_guid=package_id", + HTTPStatus.OK, + None, + "v3", "packages", "POST_response.json" + ) + result = self.client.v3.packages.copy( + package_guid="package_id", + app_guid="app-guid", + meta_labels={"key": "value"}, + meta_annotations={"note": "detailed information"}, + ) + self.client.post.assert_called_with( + self.client.post.return_value.url, + json={ + "relationships": { + "app": { + "data": { + "guid": "app-guid" + } + } + }, + "metadata": {"labels": {"key": "value"}, "annotations": {"note": "detailed information"}} + }, + files=None, + ) + self.assertIsNotNone(result) + self.assertIsInstance(result, Entity) + def test_list(self): self.client.get.return_value = self.mock_response( "/v3/packages", @@ -24,8 +86,8 @@ def test_list(self): self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(2, len(all_packages)) self.assertEqual(all_packages[0]["type"], "bits") - for droplet in all_packages: - self.assertIsInstance(droplet, Entity) + for package in all_packages: + self.assertIsInstance(package, Entity) def test_get(self): self.client.get.return_value = self.mock_response( From 39c9fe53421edc6fa6512559b8f07dbb3b661178 Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Sat, 10 Jan 2026 12:05:26 +0100 Subject: [PATCH 242/264] feature(v3) add stack manager (#287) See #280 --- cloudfoundry_client/client.py | 2 + cloudfoundry_client/v3/stacks.py | 42 +++++ tests/fixtures/v3/stacks/GET_response.json | 54 ++++++ .../v3/stacks/GET_{id}_apps_response.json | 162 ++++++++++++++++++ .../fixtures/v3/stacks/GET_{id}_response.json | 19 ++ .../v3/stacks/PATCH_{id}_response.json | 19 ++ tests/fixtures/v3/stacks/POST_response.json | 19 ++ tests/v3/test_security_groups.py | 2 +- tests/v3/test_stacks.py | 88 ++++++++++ 9 files changed, 406 insertions(+), 1 deletion(-) create mode 100644 cloudfoundry_client/v3/stacks.py create mode 100644 tests/fixtures/v3/stacks/GET_response.json create mode 100644 tests/fixtures/v3/stacks/GET_{id}_apps_response.json create mode 100644 tests/fixtures/v3/stacks/GET_{id}_response.json create mode 100644 tests/fixtures/v3/stacks/PATCH_{id}_response.json create mode 100644 tests/fixtures/v3/stacks/POST_response.json create mode 100644 tests/v3/test_stacks.py diff --git a/cloudfoundry_client/client.py b/cloudfoundry_client/client.py index 98d16f4..46944d2 100644 --- a/cloudfoundry_client/client.py +++ b/cloudfoundry_client/client.py @@ -46,6 +46,7 @@ from cloudfoundry_client.v3.service_offerings import ServiceOfferingsManager from cloudfoundry_client.v3.service_plans import ServicePlanManager from cloudfoundry_client.v3.spaces import SpaceManager +from cloudfoundry_client.v3.stacks import StackMananager from cloudfoundry_client.v3.tasks import TaskManager from cloudfoundry_client.v3.jobs import JobManager from cloudfoundry_client.v3.users import UserManager @@ -137,6 +138,7 @@ def __init__(self, cloud_controller_v3_url: str, credential_manager: "CloudFound self.service_offerings = ServiceOfferingsManager(target_endpoint, credential_manager) self.service_plans = ServicePlanManager(target_endpoint, credential_manager) self.spaces = SpaceManager(target_endpoint, credential_manager) + self.stacks = StackMananager(target_endpoint, credential_manager) self.tasks = TaskManager(target_endpoint, credential_manager) self.users = UserManager(target_endpoint, credential_manager) diff --git a/cloudfoundry_client/v3/stacks.py b/cloudfoundry_client/v3/stacks.py new file mode 100644 index 0000000..6004f2e --- /dev/null +++ b/cloudfoundry_client/v3/stacks.py @@ -0,0 +1,42 @@ +from typing import TYPE_CHECKING + +from cloudfoundry_client.common_objects import Pagination +from cloudfoundry_client.v3.entities import EntityManager, Entity + +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + + +class StackMananager(EntityManager[Entity]): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super(StackMananager, self).__init__(target_endpoint, client, "/v3/stacks") + + def create( + self, + name: str, + description: str | None = None, + meta_labels: dict | None = None, + meta_annotations: dict | None = None, + ) -> Entity: + data = {"name": name} + if description is not None: + data["description"] = description + self._metadata(data, meta_labels, meta_annotations) + return super(StackMananager, self)._create(data) + + def update( + self, + stack_guid: str, + meta_labels: dict | None = None, + meta_annotations: dict | None = None, + ) -> Entity: + data = {} + self._metadata(data, meta_labels, meta_annotations) + return super(StackMananager, self)._update(stack_guid, data) + + def list_apps(self, stack_guid: str, **kwargs) -> Pagination[Entity]: + uri: str = "%s/%s/apps" % (self.entity_uri, stack_guid) + return super(StackMananager, self)._list(requested_path=uri, **kwargs) + + def remove(self, stack_guid: str): + super(StackMananager, self)._remove(stack_guid) diff --git a/tests/fixtures/v3/stacks/GET_response.json b/tests/fixtures/v3/stacks/GET_response.json new file mode 100644 index 0000000..10aaa83 --- /dev/null +++ b/tests/fixtures/v3/stacks/GET_response.json @@ -0,0 +1,54 @@ +{ + "pagination": { + "total_results": 3, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/stacks?page=1&per_page=2" + }, + "last": { + "href": "https://api.example.org/v3/stacks?page=2&per_page=2" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "11c916c9-c2f9-440e-8e73-102e79c4704d", + "created_at": "2018-11-09T22:43:28Z", + "updated_at": "2018-11-09T22:43:28Z", + "name": "my-stack-1", + "build_rootfs_image": "my-stack-1-build", + "run_rootfs_image": "my-stack-1-run", + "description": "This is my first stack!", + "default": true, + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/stacks/11c916c9-c2f9-440e-8e73-102e79c4704d" + } + } + }, + { + "guid": "81c916c9-c2f9-440e-8e73-102e79c4704h", + "created_at": "2018-11-09T22:43:29Z", + "updated_at": "2018-11-09T22:43:29Z", + "name": "my-stack-2", + "description": "This is my second stack!", + "build_rootfs_image": "my-stack-2-build", + "run_rootfs_image": "my-stack-2-run", + "default": false, + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.org/v3/stacks/81c916c9-c2f9-440e-8e73-102e79c4704h" + } + } + } + ] +} \ No newline at end of file diff --git a/tests/fixtures/v3/stacks/GET_{id}_apps_response.json b/tests/fixtures/v3/stacks/GET_{id}_apps_response.json new file mode 100644 index 0000000..a542fa6 --- /dev/null +++ b/tests/fixtures/v3/stacks/GET_{id}_apps_response.json @@ -0,0 +1,162 @@ +{ + "pagination": { + "total_results": 3, + "total_pages": 2, + "first": { + "href": "https://api.example.org/v3/stacks/[guid]/apps?page=1&per_page=2" + }, + "last": { + "href": "https://api.example.org/v3/stacks/[guid]/apps?page=2&per_page=2" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "1cb006ee-fb05-47e1-b541-c34179ddc446", + "name": "my_app", + "state": "STARTED", + "created_at": "2016-03-17T21:41:30Z", + "updated_at": "2016-03-18T11:32:30Z", + "lifecycle": { + "type": "buildpack", + "data": { + "buildpacks": ["java_buildpack"], + "stack": "cflinuxfs4" + } + }, + "relationships": { + "space": { + "data": { + "guid": "2f35885d-0c9d-4423-83ad-fd05066f8576" + } + }, + "current_droplet": { + "data": { + "guid": "585bc3c1-3743-497d-88b0-403ad6b56d16" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446" + }, + "space": { + "href": "https://api.example.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576" + }, + "processes": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/processes" + }, + "packages": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/packages" + }, + "environment_variables": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/environment_variables" + }, + "current_droplet": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/droplets/current" + }, + "droplets": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/droplets" + }, + "tasks": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/tasks" + }, + "start": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/actions/start", + "method": "POST" + }, + "stop": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/actions/stop", + "method": "POST" + }, + "revisions": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/revisions" + }, + "deployed_revisions": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/revisions/deployed" + }, + "features": { + "href": "https://api.example.org/v3/apps/1cb006ee-fb05-47e1-b541-c34179ddc446/features" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } + }, + { + "guid": "02b4ec9b-94c7-4468-9c23-4e906191a0f8", + "name": "my_app2", + "state": "STOPPED", + "created_at": "1970-01-01T00:00:02Z", + "updated_at": "2016-06-08T16:41:26Z", + "lifecycle": { + "type": "buildpack", + "data": { + "buildpacks": ["ruby_buildpack"], + "stack": "cflinuxfs4" + } + }, + "relationships": { + "space": { + "data": { + "guid": "2f35885d-0c9d-4423-83ad-fd05066f8576" + } + }, + "droplet": { + "data": { + "guid": "585bc3c1-3743-497d-88b0-403ad6b56d16" + } + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/stacks/[guid]/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8" + }, + "space": { + "href": "https://api.example.org/v3/spaces/2f35885d-0c9d-4423-83ad-fd05066f8576" + }, + "processes": { + "href": "https://api.example.org/v3/stacks/[guid]/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/processes" + }, + "packages": { + "href": "https://api.example.org/v3/stacks/[guid]/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/packages" + }, + "environment_variables": { + "href": "https://api.example.org/v3/stacks/[guid]/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/environment_variables" + }, + "current_droplet": { + "href": "https://api.example.org/v3/stacks/[guid]/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/droplets/current" + }, + "droplets": { + "href": "https://api.example.org/v3/stacks/[guid]/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/droplets" + }, + "tasks": { + "href": "https://api.example.org/v3/stacks/[guid]/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/tasks" + }, + "start": { + "href": "https://api.example.org/v3/stacks/[guid]/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/actions/start", + "method": "POST" + }, + "stop": { + "href": "https://api.example.org/v3/stacks/[guid]/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/actions/stop", + "method": "POST" + }, + "revisions": { + "href": "https://api.example.org//v3/stacks/[guid]/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/revisions" + }, + "deployed_revisions": { + "href": "https://api.example.org//v3/stacks/[guid]/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/revisions/deployed" + }, + "features": { + "href": "https://api.example.org//v3/stacks/[guid]/apps/02b4ec9b-94c7-4468-9c23-4e906191a0f8/features" + } + }, + "metadata": { + "labels": {}, + "annotations": {} + } + } + ] + } \ No newline at end of file diff --git a/tests/fixtures/v3/stacks/GET_{id}_response.json b/tests/fixtures/v3/stacks/GET_{id}_response.json new file mode 100644 index 0000000..b6a38b4 --- /dev/null +++ b/tests/fixtures/v3/stacks/GET_{id}_response.json @@ -0,0 +1,19 @@ +{ + "guid": "11c916c9-c2f9-440e-8e73-102e79c4704d", + "created_at": "2018-11-09T22:43:28Z", + "updated_at": "2018-11-09T22:43:28Z", + "name": "my-stack", + "description": "Here is my stack!", + "build_rootfs_image": "my-stack", + "run_rootfs_image": "my-stack", + "default": true, + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.com/v3/stacks/11c916c9-c2f9-440e-8e73-102e79c4704d" + } + } +} diff --git a/tests/fixtures/v3/stacks/PATCH_{id}_response.json b/tests/fixtures/v3/stacks/PATCH_{id}_response.json new file mode 100644 index 0000000..f2e2f01 --- /dev/null +++ b/tests/fixtures/v3/stacks/PATCH_{id}_response.json @@ -0,0 +1,19 @@ +{ + "guid": "11c916c9-c2f9-440e-8e73-102e79c4704d", + "created_at": "2018-11-09T22:43:28Z", + "updated_at": "2018-11-09T22:43:28Z", + "name": "my-stack", + "description": "Here is my stack!", + "build_rootfs_image": "my-stack", + "run_rootfs_image": "my-stack", + "default": true, + "metadata": { + "labels": {"key":"value"}, + "annotations": {"note":"detailed information"} + }, + "links": { + "self": { + "href": "https://api.example.com/v3/stacks/11c916c9-c2f9-440e-8e73-102e79c4704d" + } + } +} diff --git a/tests/fixtures/v3/stacks/POST_response.json b/tests/fixtures/v3/stacks/POST_response.json new file mode 100644 index 0000000..80cce02 --- /dev/null +++ b/tests/fixtures/v3/stacks/POST_response.json @@ -0,0 +1,19 @@ +{ + "guid": "11c916c9-c2f9-440e-8e73-102e79c4704d", + "created_at": "2018-11-09T22:43:28Z", + "updated_at": "2018-11-09T22:43:28Z", + "name": "my-stack", + "description": "Here is my stack!", + "build_rootfs_image": "my-stack", + "run_rootfs_image": "my-stack", + "default": true, + "metadata": { + "labels": {}, + "annotations": {} + }, + "links": { + "self": { + "href": "https://api.example.com/v3/stacks/11c916c9-c2f9-440e-8e73-102e79c4704d" + } + } +} \ No newline at end of file diff --git a/tests/v3/test_security_groups.py b/tests/v3/test_security_groups.py index 885ae12..6670a15 100644 --- a/tests/v3/test_security_groups.py +++ b/tests/v3/test_security_groups.py @@ -42,7 +42,7 @@ def test_list(self): self.client.get.return_value = self.mock_response( "/v3/security_groups", HTTPStatus.OK, None, "v3", "security_groups", "GET_response.json" ) - all_security_groups = [service_broker for service_broker in self.client.v3.security_groups.list()] + all_security_groups = [security_group for security_group in self.client.v3.security_groups.list()] self.client.get.assert_called_with(self.client.get.return_value.url) self.assertEqual(2, len(all_security_groups)) self.assertEqual(all_security_groups[0]["name"], "my-group0") diff --git a/tests/v3/test_stacks.py b/tests/v3/test_stacks.py new file mode 100644 index 0000000..a5cd581 --- /dev/null +++ b/tests/v3/test_stacks.py @@ -0,0 +1,88 @@ +import unittest +from http import HTTPStatus + +from abstract_test_case import AbstractTestCase +from cloudfoundry_client.v3.entities import Entity + + +class TestStacks(unittest.TestCase, AbstractTestCase): + @classmethod + def setUpClass(cls): + cls.mock_client_class() + + def setUp(self): + self.build_client() + + def test_create(self): + self.client.post.return_value = self.mock_response( + "/v3/stacks", HTTPStatus.CREATED, None, "v3", "stacks", "POST_response.json" + ) + result = self.client.v3.stacks.create("my-stack", "Here is my stack!") + self.client.post.assert_called_with( + self.client.post.return_value.url, + json={ + "name": "my-stack", + "description": "Here is my stack!", + }, + files=None + ) + self.assertIsNotNone(result) + self.assertIsInstance(result, Entity) + + def test_list(self): + self.client.get.return_value = self.mock_response( + "/v3/stacks", HTTPStatus.OK, None, "v3", "stacks", "GET_response.json" + ) + all_stacks = [stack for stack in self.client.v3.stacks.list()] + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual(2, len(all_stacks)) + self.assertEqual(all_stacks[0]["name"], "my-stack-1") + self.assertIsInstance(all_stacks[0], Entity) + + def test_get(self): + self.client.get.return_value = self.mock_response( + "/v3/stacks/stack-id", HTTPStatus.OK, None, "v3", "stacks", + "GET_{id}_response.json" + ) + stack = self.client.v3.stacks.get("stack-id") + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual("my-stack", stack["name"]) + self.assertIsInstance(stack, Entity) + + def test_update(self): + self.client.patch.return_value = self.mock_response( + "/v3/stacks/stack-id", HTTPStatus.OK, None, + "v3", "stacks", "PATCH_{id}_response.json" + ) + result = self.client.v3.stacks.update("stack-id", + {"key": "value"}, + {"note": "detailed information"} + ) + self.client.patch.assert_called_with( + self.client.patch.return_value.url, + json={ + "metadata": { + "labels": {"key": "value"}, + "annotations": {"note": "detailed information"} + } + } + ) + self.assertIsNotNone(result) + self.assertIsInstance(result, Entity) + + def test_list_apps(self): + self.client.get.return_value = self.mock_response( + "/v3/stacks/stack-id/apps", HTTPStatus.OK, None, + "v3", "stacks", "GET_{id}_apps_response.json" + ) + all_apps = [app for app in self.client.v3.stacks.list_apps('stack-id')] + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual(2, len(all_apps)) + self.assertEqual(all_apps[0]["name"], "my_app") + self.assertIsInstance(all_apps[0], Entity) + + def test_remove(self): + self.client.delete.return_value = self.mock_response("/v3/stacks/stack-id", + HTTPStatus.NO_CONTENT, None) + self.client.v3.stacks.remove("stack-id") + self.client.delete.assert_called_with(self.client.delete.return_value.url) From a2f7f482ce940b397ec1a37f357ae40160393323 Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Sat, 10 Jan 2026 12:20:26 +0100 Subject: [PATCH 243/264] chore(code) remooving explicit typing for super() call (#288) --- cloudfoundry_client/client.py | 2 +- cloudfoundry_client/common_objects.py | 4 ++-- cloudfoundry_client/errors.py | 2 +- cloudfoundry_client/main/apps_command_domain.py | 2 +- cloudfoundry_client/main/tasks_command_domain.py | 2 +- cloudfoundry_client/v2/apps.py | 10 +++++----- cloudfoundry_client/v2/buildpacks.py | 4 ++-- cloudfoundry_client/v2/entities.py | 2 +- cloudfoundry_client/v2/events.py | 2 +- cloudfoundry_client/v2/routes.py | 8 ++++---- cloudfoundry_client/v2/service_bindings.py | 6 +++--- cloudfoundry_client/v2/service_brokers.py | 8 ++++---- cloudfoundry_client/v2/service_instances.py | 10 +++++----- cloudfoundry_client/v2/service_keys.py | 6 +++--- .../v2/service_plan_visibilities.py | 8 ++++---- cloudfoundry_client/v2/service_plans.py | 2 +- cloudfoundry_client/v2/spaces.py | 4 ++-- cloudfoundry_client/v3/apps.py | 8 +++----- cloudfoundry_client/v3/buildpacks.py | 12 ++++++------ cloudfoundry_client/v3/domains.py | 14 +++++++------- cloudfoundry_client/v3/feature_flags.py | 4 ++-- cloudfoundry_client/v3/isolation_segments.py | 12 ++++++------ cloudfoundry_client/v3/jobs.py | 2 +- cloudfoundry_client/v3/organizations.py | 6 +++--- cloudfoundry_client/v3/processes.py | 2 +- cloudfoundry_client/v3/roles.py | 4 ++-- cloudfoundry_client/v3/security_groups.py | 2 +- cloudfoundry_client/v3/service_brokers.py | 8 ++++---- .../v3/service_credential_bindings.py | 9 ++++----- cloudfoundry_client/v3/service_instances.py | 6 +++--- cloudfoundry_client/v3/service_offerings.py | 6 +++--- cloudfoundry_client/v3/service_plans.py | 14 +++++++------- cloudfoundry_client/v3/spaces.py | 8 ++++---- cloudfoundry_client/v3/tasks.py | 2 +- tests/abstract_test_case.py | 2 +- 35 files changed, 100 insertions(+), 103 deletions(-) diff --git a/cloudfoundry_client/client.py b/cloudfoundry_client/client.py index 46944d2..9046c2c 100644 --- a/cloudfoundry_client/client.py +++ b/cloudfoundry_client/client.py @@ -171,7 +171,7 @@ def __init__(self, target_endpoint: str, client_id: str = "cf", client_secret: s service_information = ServiceInformation( None, "%s/oauth/token" % info.authorization_endpoint, client_id, client_secret, [], verify ) - super(CloudFoundryClient, self).__init__( + super().__init__( service_information, proxies=proxy, user_agent=kwargs.get("user_agent", "cf-python-client") diff --git a/cloudfoundry_client/common_objects.py b/cloudfoundry_client/common_objects.py index 57d97e5..4a03083 100644 --- a/cloudfoundry_client/common_objects.py +++ b/cloudfoundry_client/common_objects.py @@ -6,12 +6,12 @@ class Request(dict): def __setitem__(self, key, value): if value is not None: - super(Request, self).__setitem__(key, value) + super().__setitem__(key, value) class JsonObject(dict): def __init__(self, *args, **kwargs): - super(JsonObject, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) json = json.dumps diff --git a/cloudfoundry_client/errors.py b/cloudfoundry_client/errors.py index 684db73..41f8910 100644 --- a/cloudfoundry_client/errors.py +++ b/cloudfoundry_client/errors.py @@ -25,7 +25,7 @@ def __str__(self): class InvalidEntity(Exception): def __init__(self, **kwargs): - super(InvalidEntity, self).__init__() + super().__init__() self.raw_entity = dict(**kwargs) def __str__(self): diff --git a/cloudfoundry_client/main/apps_command_domain.py b/cloudfoundry_client/main/apps_command_domain.py index 206ae2e..641c6f4 100644 --- a/cloudfoundry_client/main/apps_command_domain.py +++ b/cloudfoundry_client/main/apps_command_domain.py @@ -7,7 +7,7 @@ class AppCommandDomain(CommandDomain): def __init__(self): - super(AppCommandDomain, self).__init__( + super().__init__( display_name="Applications", entity_name="app", filter_list_parameters=["organization_guid", "space_guid"], diff --git a/cloudfoundry_client/main/tasks_command_domain.py b/cloudfoundry_client/main/tasks_command_domain.py index 9a7c623..8dcdbdc 100644 --- a/cloudfoundry_client/main/tasks_command_domain.py +++ b/cloudfoundry_client/main/tasks_command_domain.py @@ -9,7 +9,7 @@ class TaskCommandDomain(CommandDomain): def __init__(self): - super(TaskCommandDomain, self).__init__( + super().__init__( display_name="Tasks", entity_name="task", filter_list_parameters=["names", "app_guids", "space_guids", "organization_guids"], diff --git a/cloudfoundry_client/v2/apps.py b/cloudfoundry_client/v2/apps.py index ebb8379..bd78961 100644 --- a/cloudfoundry_client/v2/apps.py +++ b/cloudfoundry_client/v2/apps.py @@ -76,7 +76,7 @@ class AppManager(EntityManager): ] def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(AppManager, self).__init__( + super().__init__( target_endpoint, client, "/v2/apps", lambda pairs: Application(target_endpoint, client, pairs) ) @@ -111,7 +111,7 @@ def start( timeout: float | None = 300.0, asynchronous: bool | None = False, ) -> Application: - result = super(AppManager, self)._update(application_guid, dict(state="STARTED")) + result = super()._update(application_guid, dict(state="STARTED")) if asynchronous: return result else: @@ -126,7 +126,7 @@ def stop( timeout: float | None = 500.0, asynchronous: bool | None = False, ) -> Application: - result = super(AppManager, self)._update(application_guid, dict(state="STOPPED")) + result = super()._update(application_guid, dict(state="STOPPED")) if asynchronous: return result else: @@ -143,11 +143,11 @@ def create(self, **kwargs) -> Application: if kwargs.get("name") is None or kwargs.get("space_guid") is None: raise AssertionError("Please provide a name and a space_guid") request = AppManager._generate_application_update_request(**kwargs) - return super(AppManager, self)._create(request) + return super()._create(request) def update(self, application_guid: str, **kwargs) -> Application: request = AppManager._generate_application_update_request(**kwargs) - return super(AppManager, self)._update(application_guid, request) + return super()._update(application_guid, request) def remove(self, application_guid: str): super(AppManager, self)._remove(application_guid) diff --git a/cloudfoundry_client/v2/buildpacks.py b/cloudfoundry_client/v2/buildpacks.py index 958a283..bf71e09 100644 --- a/cloudfoundry_client/v2/buildpacks.py +++ b/cloudfoundry_client/v2/buildpacks.py @@ -8,7 +8,7 @@ class BuildpackManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(BuildpackManager, self).__init__(target_endpoint, client, "/v2/buildpacks") + super().__init__(target_endpoint, client, "/v2/buildpacks") def update(self, buildpack_guid: str, parameters: dict) -> Entity: - return super(BuildpackManager, self)._update(buildpack_guid, parameters) + return super()._update(buildpack_guid, parameters) diff --git a/cloudfoundry_client/v2/entities.py b/cloudfoundry_client/v2/entities.py index e71440e..8cd1142 100644 --- a/cloudfoundry_client/v2/entities.py +++ b/cloudfoundry_client/v2/entities.py @@ -13,7 +13,7 @@ class Entity(JsonObject): def __init__(self, target_endpoint: str, client: "CloudFoundryClient", *args, **kwargs): - super(Entity, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.target_endpoint = target_endpoint self.client = client try: diff --git a/cloudfoundry_client/v2/events.py b/cloudfoundry_client/v2/events.py index c71e802..9edd455 100644 --- a/cloudfoundry_client/v2/events.py +++ b/cloudfoundry_client/v2/events.py @@ -9,7 +9,7 @@ class EventManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(EventManager, self).__init__(target_endpoint, client, "/v2/events") + super().__init__(target_endpoint, client, "/v2/events") def list_by_type(self, event_type: str) -> Generator[Entity, None, None]: return self._list(self.entity_uri, type=event_type) diff --git a/cloudfoundry_client/v2/routes.py b/cloudfoundry_client/v2/routes.py index 4fdcf92..180d52f 100644 --- a/cloudfoundry_client/v2/routes.py +++ b/cloudfoundry_client/v2/routes.py @@ -8,16 +8,16 @@ class RouteManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(RouteManager, self).__init__(target_endpoint, client, "/v2/routes") + super().__init__(target_endpoint, client, "/v2/routes") def create_tcp_route(self, domain_guid: str, space_guid: str, port: int | None = None) -> Entity: request = self._request(domain_guid=domain_guid, space_guid=space_guid) if port is None: - return super(RouteManager, self)._create(request, params=dict(generate_port=True)) + return super()._create(request, params=dict(generate_port=True)) else: request["port"] = port - return super(RouteManager, self)._create(request) + return super()._create(request) def create_host_route(self, domain_guid: str, space_guid: str, host: str, path: str | None = "") -> Entity: request = dict(domain_guid=domain_guid, space_guid=space_guid, host=host, path=path) - return super(RouteManager, self)._create(request) + return super()._create(request) diff --git a/cloudfoundry_client/v2/service_bindings.py b/cloudfoundry_client/v2/service_bindings.py index 21f4e80..a1cb45f 100644 --- a/cloudfoundry_client/v2/service_bindings.py +++ b/cloudfoundry_client/v2/service_bindings.py @@ -8,13 +8,13 @@ class ServiceBindingManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceBindingManager, self).__init__(target_endpoint, client, "/v2/service_bindings") + super().__init__(target_endpoint, client, "/v2/service_bindings") def create(self, app_guid: str, instance_guid: str, parameters: dict | None = None, name: str | None = None) -> Entity: request = self._request(app_guid=app_guid, service_instance_guid=instance_guid) request["parameters"] = parameters request["name"] = name - return super(ServiceBindingManager, self)._create(request) + return super()._create(request) def remove(self, binding_id: str): - super(ServiceBindingManager, self)._remove(binding_id) + super()._remove(binding_id) diff --git a/cloudfoundry_client/v2/service_brokers.py b/cloudfoundry_client/v2/service_brokers.py index b29e8b5..512f807 100644 --- a/cloudfoundry_client/v2/service_brokers.py +++ b/cloudfoundry_client/v2/service_brokers.py @@ -8,14 +8,14 @@ class ServiceBrokerManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceBrokerManager, self).__init__(target_endpoint, client, "/v2/service_brokers") + super().__init__(target_endpoint, client, "/v2/service_brokers") def create( self, broker_url: str, broker_name: str, auth_username: str, auth_password: str, space_guid: str | None = None ) -> Entity: request = self._request(broker_url=broker_url, name=broker_name, auth_username=auth_username, auth_password=auth_password) request["space_guid"] = space_guid - return super(ServiceBrokerManager, self)._create(request) + return super()._create(request) def update( self, @@ -30,7 +30,7 @@ def update( request["name"] = broker_name request["auth_username"] = auth_username request["auth_password"] = auth_password - return super(ServiceBrokerManager, self)._update(broker_guid, request) + return super()._update(broker_guid, request) def remove(self, broker_guid): - super(ServiceBrokerManager, self)._remove(broker_guid) + super()._remove(broker_guid) diff --git a/cloudfoundry_client/v2/service_instances.py b/cloudfoundry_client/v2/service_instances.py index 8213572..f659986 100644 --- a/cloudfoundry_client/v2/service_instances.py +++ b/cloudfoundry_client/v2/service_instances.py @@ -10,7 +10,7 @@ class ServiceInstanceManager(EntityManager): list_query_parameters = ["page", "results-per-page", "order-direction", "return_user_provided_service_instances"] def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceInstanceManager, self).__init__(target_endpoint, client, "/v2/service_instances") + super().__init__(target_endpoint, client, "/v2/service_instances") def create( self, @@ -25,7 +25,7 @@ def create( request["parameters"] = parameters request["tags"] = tags params = None if not accepts_incomplete else dict(accepts_incomplete="true") - return super(ServiceInstanceManager, self)._create(request, params=params) + return super()._create(request, params=params) def update( self, @@ -42,10 +42,10 @@ def update( request["parameters"] = parameters request["tags"] = tags params = None if not accepts_incomplete else dict(accepts_incomplete="true") - return super(ServiceInstanceManager, self)._update(instance_guid, request, params=params) + return super()._update(instance_guid, request, params=params) def list_permissions(self, instance_guid: str) -> dict[str, bool]: - return super(ServiceInstanceManager, self)._get("%s/%s/permissions" % (self.entity_uri, instance_guid), dict) + return super()._get("%s/%s/permissions" % (self.entity_uri, instance_guid), dict) def remove(self, instance_guid: str, accepts_incomplete: bool | None = False, purge: bool | None = False): parameters = {} @@ -53,4 +53,4 @@ def remove(self, instance_guid: str, accepts_incomplete: bool | None = False, pu parameters["accepts_incomplete"] = "true" if purge: parameters["purge"] = "true" - super(ServiceInstanceManager, self)._remove(instance_guid, params=parameters) + super()._remove(instance_guid, params=parameters) diff --git a/cloudfoundry_client/v2/service_keys.py b/cloudfoundry_client/v2/service_keys.py index d807f1a..314b3e8 100644 --- a/cloudfoundry_client/v2/service_keys.py +++ b/cloudfoundry_client/v2/service_keys.py @@ -8,12 +8,12 @@ class ServiceKeyManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceKeyManager, self).__init__(target_endpoint, client, "/v2/service_keys") + super().__init__(target_endpoint, client, "/v2/service_keys") def create(self, service_instance_guid: str, name: str, parameters: dict | None = None) -> Entity: request = self._request(service_instance_guid=service_instance_guid, name=name) request["parameters"] = parameters - return super(ServiceKeyManager, self)._create(request) + return super()._create(request) def remove(self, key_guid: str): - super(ServiceKeyManager, self)._remove(key_guid) + super()._remove(key_guid) diff --git a/cloudfoundry_client/v2/service_plan_visibilities.py b/cloudfoundry_client/v2/service_plan_visibilities.py index 4d7d513..f882495 100644 --- a/cloudfoundry_client/v2/service_plan_visibilities.py +++ b/cloudfoundry_client/v2/service_plan_visibilities.py @@ -8,19 +8,19 @@ class ServicePlanVisibilityManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServicePlanVisibilityManager, self).__init__(target_endpoint, client, "/v2/service_plan_visibilities") + super().__init__(target_endpoint, client, "/v2/service_plan_visibilities") def create(self, service_plan_guid: str, organization_guid: str) -> Entity: request = self._request() request["service_plan_guid"] = service_plan_guid request["organization_guid"] = organization_guid - return super(ServicePlanVisibilityManager, self)._create(request) + return super()._create(request) def update(self, spv_guid: str, service_plan_guid: str, organization_guid: str) -> Entity: request = self._request() request["service_plan_guid"] = service_plan_guid request["organization_guid"] = organization_guid - return super(ServicePlanVisibilityManager, self)._update(spv_guid, request) + return super()._update(spv_guid, request) def remove(self, spv_guid: str): - super(ServicePlanVisibilityManager, self)._remove(spv_guid) + super()._remove(spv_guid) diff --git a/cloudfoundry_client/v2/service_plans.py b/cloudfoundry_client/v2/service_plans.py index 9a94aa8..48c54f4 100644 --- a/cloudfoundry_client/v2/service_plans.py +++ b/cloudfoundry_client/v2/service_plans.py @@ -9,7 +9,7 @@ class ServicePlanManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServicePlanManager, self).__init__(target_endpoint, client, "/v2/service_plans") + super().__init__(target_endpoint, client, "/v2/service_plans") def create_from_resource_file(self, path: str) -> Entity: raise NotImplementedError("No creation allowed") diff --git a/cloudfoundry_client/v2/spaces.py b/cloudfoundry_client/v2/spaces.py index 6f6254a..24f0745 100644 --- a/cloudfoundry_client/v2/spaces.py +++ b/cloudfoundry_client/v2/spaces.py @@ -8,8 +8,8 @@ class SpaceManager(EntityManager): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(SpaceManager, self).__init__(target_endpoint, client, "/v2/spaces") + super().__init__(target_endpoint, client, "/v2/spaces") def delete_unmapped_routes(self, space_guid: str): url = "%s%s/%s/unmapped_routes" % (self.target_endpoint, self.entity_uri, space_guid) - super(SpaceManager, self)._delete(url) + super()._delete(url) diff --git a/cloudfoundry_client/v3/apps.py b/cloudfoundry_client/v3/apps.py index 3357164..6b309d1 100644 --- a/cloudfoundry_client/v3/apps.py +++ b/cloudfoundry_client/v3/apps.py @@ -9,15 +9,13 @@ class AppManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(AppManager, self).__init__(target_endpoint, client, "/v3/apps") + super().__init__(target_endpoint, client, "/v3/apps") def restart(self, application_guid: str): - return super(AppManager, self)._post("%s%s/%s/actions/restart" % (self.target_endpoint, - self.entity_uri, - application_guid)) + return super()._post("%s%s/%s/actions/restart" % (self.target_endpoint, self.entity_uri, application_guid)) def remove(self, application_guid: str, asynchronous: bool = True) -> str | None: - return super(AppManager, self)._remove(application_guid, asynchronous) + return super()._remove(application_guid, asynchronous) def get_env(self, application_guid: str) -> JsonObject: return super(AppManager, self)._get("%s%s/%s/env" % (self.target_endpoint, self.entity_uri, application_guid)) diff --git a/cloudfoundry_client/v3/buildpacks.py b/cloudfoundry_client/v3/buildpacks.py index aa88ab8..37c71fc 100644 --- a/cloudfoundry_client/v3/buildpacks.py +++ b/cloudfoundry_client/v3/buildpacks.py @@ -8,7 +8,7 @@ class BuildpackManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(BuildpackManager, self).__init__(target_endpoint, client, "/v3/buildpacks") + super().__init__(target_endpoint, client, "/v3/buildpacks") def create( self, @@ -28,10 +28,10 @@ def create( "stack": stack, } self._metadata(data, meta_labels, meta_annotations) - return super(BuildpackManager, self)._create(data) + return super()._create(data) def remove(self, buildpack_guid: str, asynchronous: bool = True) -> str | None: - return super(BuildpackManager, self)._remove(buildpack_guid, asynchronous) + return super()._remove(buildpack_guid, asynchronous) def update( self, @@ -52,13 +52,13 @@ def update( "stack": stack, } self._metadata(data, meta_labels, meta_annotations) - return super(BuildpackManager, self)._update(buildpack_guid, data) + return super()._update(buildpack_guid, data) def upload(self, buildpack_guid: str, buildpack_zip: str, asynchronous: bool = False) -> Entity: - buildpack = super(BuildpackManager, self)._upload_bits(buildpack_guid, buildpack_zip) + buildpack = super()._upload_bits(buildpack_guid, buildpack_zip) if not asynchronous: self.client.v3.jobs.wait_for_job_completion(buildpack.job()["guid"]) - buildpack_after_job = super(BuildpackManager, self).get(buildpack["guid"]) + buildpack_after_job = super().get(buildpack["guid"]) buildpack_after_job["links"]["job"] = buildpack["links"]["job"] buildpack_after_job.job = buildpack.job return buildpack_after_job diff --git a/cloudfoundry_client/v3/domains.py b/cloudfoundry_client/v3/domains.py index a26bea9..23fcba0 100644 --- a/cloudfoundry_client/v3/domains.py +++ b/cloudfoundry_client/v3/domains.py @@ -9,7 +9,7 @@ class Domain(Entity): def __init__(self, target_endpoint: str, client: "CloudFoundryClient", **kwargs): - super(Domain, self).__init__(target_endpoint, client, **kwargs) + super().__init__(target_endpoint, client, **kwargs) relationships = self["relationships"] if "organization" in relationships: self["relationships"]["organization"] = ToOneRelationship.from_json_object(relationships["organization"]) @@ -21,7 +21,7 @@ def __init__(self, target_endpoint: str, client: "CloudFoundryClient", **kwargs) class DomainManager(EntityManager[Domain]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(DomainManager, self).__init__(target_endpoint, client, "/v3/domains", Domain) + super().__init__(target_endpoint, client, "/v3/domains", Domain) def create( self, @@ -41,7 +41,7 @@ def create( }, } self._metadata(data, meta_labels, meta_annotations) - return super(DomainManager, self)._create(data) + return super()._create(data) def list_domains_for_org(self, org_guid: str, **kwargs) -> Pagination[Entity]: uri = "/v3/organizations/{guid}/domains".format(guid=org_guid) @@ -49,10 +49,10 @@ def list_domains_for_org(self, org_guid: str, **kwargs) -> Pagination[Entity]: def update(self, domain_guid: str, meta_labels: dict | None = None, meta_annotations: dict | None = None) -> Domain: data = {"metadata": {"labels": meta_labels, "annotations": meta_annotations}} - return super(DomainManager, self)._update(domain_guid, data) + return super()._update(domain_guid, data) def remove(self, domain_guid: str, asynchronous: bool = True) -> str | None: - return super(DomainManager, self)._remove(domain_guid, asynchronous) + return super()._remove(domain_guid, asynchronous) def __create_shared_domain_url(self, domain_guid: str) -> str: # TODO use url parser for this @@ -62,8 +62,8 @@ def __create_shared_domain_url(self, domain_guid: str) -> str: def share_domain(self, domain_guid: str, organization_guids: ToManyRelationship) -> ToManyRelationship: url = self.__create_shared_domain_url(domain_guid) - return ToManyRelationship.from_json_object(super(DomainManager, self)._post(url, data=organization_guids)) + return ToManyRelationship.from_json_object(super()._post(url, data=organization_guids)) def unshare_domain(self, domain_guid: str, org_guid: str): url = "{uri}/{org}".format(uri=self.__create_shared_domain_url(domain_guid), org=org_guid) - super(DomainManager, self)._delete(url) + super()._delete(url) diff --git a/cloudfoundry_client/v3/feature_flags.py b/cloudfoundry_client/v3/feature_flags.py index 55717fb..c92abfd 100644 --- a/cloudfoundry_client/v3/feature_flags.py +++ b/cloudfoundry_client/v3/feature_flags.py @@ -8,8 +8,8 @@ class FeatureFlagManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(FeatureFlagManager, self).__init__(target_endpoint, client, "/v3/feature_flags") + super().__init__(target_endpoint, client, "/v3/feature_flags") def update(self, name: str, enabled: bool | None = True, custom_error_message: str | None = None) -> Entity: data = {"enabled": enabled, "custom_error_message": custom_error_message} - return super(FeatureFlagManager, self)._update(name, data) + return super()._update(name, data) diff --git a/cloudfoundry_client/v3/isolation_segments.py b/cloudfoundry_client/v3/isolation_segments.py index eafd874..a257b6f 100644 --- a/cloudfoundry_client/v3/isolation_segments.py +++ b/cloudfoundry_client/v3/isolation_segments.py @@ -8,24 +8,24 @@ class IsolationSegmentManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(IsolationSegmentManager, self).__init__(target_endpoint, client, "/v3/isolation_segments") + super().__init__(target_endpoint, client, "/v3/isolation_segments") def create(self, name: str, meta_labels: dict | None = None, meta_annotations: dict | None = None) -> Entity: data = {"name": name} self._metadata(data, meta_labels, meta_annotations) - return super(IsolationSegmentManager, self)._create(data) + return super()._create(data) def update( self, isolation_segment_guid: str, name: str, meta_labels: dict | None = None, meta_annotations: dict | None = None ) -> Entity: data = {"name": name} self._metadata(data, meta_labels, meta_annotations) - return super(IsolationSegmentManager, self)._update(isolation_segment_guid, data) + return super()._update(isolation_segment_guid, data) def entitle_organizations(self, isolation_segment_guid: str, *org_guids: str) -> ToManyRelationship: data = ToManyRelationship(*org_guids) return ToManyRelationship.from_json_object( - super(IsolationSegmentManager, self)._post( + super()._post( "%s%s/%s/relationships/organizations" % (self.target_endpoint, self.entity_uri, isolation_segment_guid), data=data ) ) @@ -35,7 +35,7 @@ def list_entitled_organizations( isolation_segment_guid: str, ) -> ToManyRelationship: return ToManyRelationship.from_json_object( - super(IsolationSegmentManager, self)._get( + super()._get( "%s%s/%s/relationships/organizations" % (self.target_endpoint, self.entity_uri, isolation_segment_guid) ) ) @@ -45,7 +45,7 @@ def list_entitled_spaces( isolation_segment_guid: str, ) -> ToManyRelationship: return ToManyRelationship.from_json_object( - super(IsolationSegmentManager, self)._get( + super()._get( "%s%s/%s/relationships/spaces" % (self.target_endpoint, self.entity_uri, isolation_segment_guid) ) ) diff --git a/cloudfoundry_client/v3/jobs.py b/cloudfoundry_client/v3/jobs.py index 9988886..d5a309a 100644 --- a/cloudfoundry_client/v3/jobs.py +++ b/cloudfoundry_client/v3/jobs.py @@ -15,7 +15,7 @@ class JobTimeout(Exception): class JobManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(JobManager, self).__init__(target_endpoint, client, "/v3/jobs") + super().__init__(target_endpoint, client, "/v3/jobs") def wait_for_job_completion( self, diff --git a/cloudfoundry_client/v3/organizations.py b/cloudfoundry_client/v3/organizations.py index 7dbacc4..2a325e8 100644 --- a/cloudfoundry_client/v3/organizations.py +++ b/cloudfoundry_client/v3/organizations.py @@ -8,14 +8,14 @@ class OrganizationManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(OrganizationManager, self).__init__(target_endpoint, client, "/v3/organizations") + super().__init__(target_endpoint, client, "/v3/organizations") def create( self, name: str, suspended: bool, meta_labels: dict | None = None, meta_annotations: dict | None = None ) -> Entity: data = {"name": name, "suspended": suspended} self._metadata(data, meta_labels, meta_annotations) - return super(OrganizationManager, self)._create(data) + return super()._create(data) def update( self, @@ -29,7 +29,7 @@ def update( if suspended is not None: data["suspended"] = suspended self._metadata(data, meta_labels, meta_annotations) - return super(OrganizationManager, self)._update(guid, data) + return super()._update(guid, data) def remove(self, guid: str, asynchronous: bool = True) -> str | None: return super(OrganizationManager, self)._remove(guid, asynchronous) diff --git a/cloudfoundry_client/v3/processes.py b/cloudfoundry_client/v3/processes.py index 34af872..7a5065b 100644 --- a/cloudfoundry_client/v3/processes.py +++ b/cloudfoundry_client/v3/processes.py @@ -8,4 +8,4 @@ class ProcessManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ProcessManager, self).__init__(target_endpoint, client, "/v3/processes") + super().__init__(target_endpoint, client, "/v3/processes") diff --git a/cloudfoundry_client/v3/roles.py b/cloudfoundry_client/v3/roles.py index 11e8eba..8771a94 100644 --- a/cloudfoundry_client/v3/roles.py +++ b/cloudfoundry_client/v3/roles.py @@ -8,7 +8,7 @@ class RoleManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(RoleManager, self).__init__(target_endpoint, client, "/v3/roles") + super().__init__(target_endpoint, client, "/v3/roles") def remove(self, role_guid: str, asynchronous: bool = True) -> str | None: - return super(RoleManager, self)._remove(role_guid, asynchronous) + return super()._remove(role_guid, asynchronous) diff --git a/cloudfoundry_client/v3/security_groups.py b/cloudfoundry_client/v3/security_groups.py index 6a21459..77e766b 100644 --- a/cloudfoundry_client/v3/security_groups.py +++ b/cloudfoundry_client/v3/security_groups.py @@ -37,7 +37,7 @@ class GloballyEnabled: class SecurityGroupManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(SecurityGroupManager, self).__init__(target_endpoint, client, "/v3/security_groups") + super().__init__(target_endpoint, client, "/v3/security_groups") def create(self, name: str, diff --git a/cloudfoundry_client/v3/service_brokers.py b/cloudfoundry_client/v3/service_brokers.py index 14e15e7..ba0b661 100644 --- a/cloudfoundry_client/v3/service_brokers.py +++ b/cloudfoundry_client/v3/service_brokers.py @@ -8,7 +8,7 @@ class ServiceBrokerManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceBrokerManager, self).__init__(target_endpoint, client, "/v3/service_brokers") + super().__init__(target_endpoint, client, "/v3/service_brokers") def create( self, @@ -25,7 +25,7 @@ def create( self._metadata(payload, meta_labels, meta_annotations) if space_guid: payload["relationships"] = dict(space=ToOneRelationship(space_guid)) - return super(ServiceBrokerManager, self)._create(payload) + return super()._create(payload) def update( self, @@ -45,7 +45,7 @@ def update( if auth_username and auth_password: payload["authentication"] = {"type": "basic", "credentials": {"username": auth_username, "password": auth_password}} self._metadata(payload, meta_labels, meta_annotations) - return super(ServiceBrokerManager, self)._update(guid, payload) + return super()._update(guid, payload) def remove(self, guid: str, asynchronous: bool = True) -> str | None: - return super(ServiceBrokerManager, self)._remove(guid, asynchronous) + return super()._remove(guid, asynchronous) diff --git a/cloudfoundry_client/v3/service_credential_bindings.py b/cloudfoundry_client/v3/service_credential_bindings.py index b1f53d5..da17322 100644 --- a/cloudfoundry_client/v3/service_credential_bindings.py +++ b/cloudfoundry_client/v3/service_credential_bindings.py @@ -8,8 +8,7 @@ class ServiceCredentialBindingManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceCredentialBindingManager, self).__init__(target_endpoint, client, - "/v3/service_credential_bindings") + super().__init__(target_endpoint, client, "/v3/service_credential_bindings") def create( self, @@ -40,12 +39,12 @@ def create( data["metadata"] = metadata url = "%s%s" % (self.target_endpoint, self.entity_uri) response = self.client.post(url, json=data) - location = super(ServiceCredentialBindingManager, self)._location(response) + location = super()._location(response) if location: - job_guid = super(ServiceCredentialBindingManager, self)._extract_job_guid(location) + job_guid = super()._extract_job_guid(location) if asynchronous: return job_guid else: self.client.v3.jobs.wait_for_job_completion(job_guid) return None - return super(ServiceCredentialBindingManager, self)._read_response(response, None) + return super()._read_response(response, None) diff --git a/cloudfoundry_client/v3/service_instances.py b/cloudfoundry_client/v3/service_instances.py index 0d3d5d4..c445c91 100644 --- a/cloudfoundry_client/v3/service_instances.py +++ b/cloudfoundry_client/v3/service_instances.py @@ -9,7 +9,7 @@ class ServiceInstanceManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceInstanceManager, self).__init__(target_endpoint, client, "/v3/service_instances") + super().__init__(target_endpoint, client, "/v3/service_instances") def create( self, @@ -31,7 +31,7 @@ def create( if tags: data["tags"] = tags self._metadata(data, meta_labels, meta_annotations) - return super(ServiceInstanceManager, self)._create(data) + return super()._create(data) def update( self, @@ -63,6 +63,6 @@ def remove(self, guid: str, asynchronous: bool = True): super()._remove(guid, asynchronous) def get_permissions(self, instance_guid: str) -> JsonObject: - return super(ServiceInstanceManager, self)._get( + return super()._get( "%s%s/%s/permissions" % (self.target_endpoint, self.entity_uri, instance_guid) ) diff --git a/cloudfoundry_client/v3/service_offerings.py b/cloudfoundry_client/v3/service_offerings.py index 76ebc7a..5e6d3aa 100644 --- a/cloudfoundry_client/v3/service_offerings.py +++ b/cloudfoundry_client/v3/service_offerings.py @@ -8,15 +8,15 @@ class ServiceOfferingsManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServiceOfferingsManager, self).__init__(target_endpoint, client, "/v3/service_offerings") + super().__init__(target_endpoint, client, "/v3/service_offerings") def update(self, guid: str, meta_labels: dict | None = None, meta_annotations: dict | None = None) -> Entity: payload = dict() self._metadata(payload, meta_labels, meta_annotations) - return super(ServiceOfferingsManager, self)._update(guid, payload) + return super()._update(guid, payload) def remove(self, guid: str, purge: bool = False) -> None: url = f"{self.target_endpoint}{self.entity_uri}/{guid}" if purge: url += "?purge=true" - super(ServiceOfferingsManager, self)._delete(url) + super()._delete(url) diff --git a/cloudfoundry_client/v3/service_plans.py b/cloudfoundry_client/v3/service_plans.py index 3f4f6fc..fce669b 100644 --- a/cloudfoundry_client/v3/service_plans.py +++ b/cloudfoundry_client/v3/service_plans.py @@ -8,7 +8,7 @@ class ServicePlanManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(ServicePlanManager, self).__init__(target_endpoint, client, "/v3/service_plans") + super().__init__(target_endpoint, client, "/v3/service_plans") def update( self, @@ -18,13 +18,13 @@ def update( ) -> Entity: payload = {"metadata": {}} self._metadata(payload, meta_labels, meta_annotations) - return super(ServicePlanManager, self)._update(guid, payload) + return super()._update(guid, payload) def remove(self, guid: str): - super(ServicePlanManager, self)._remove(guid) + super()._remove(guid) def get_visibility(self, service_plan_guid: str) -> dict: - return super(ServicePlanManager, self)._get(f"{self.target_endpoint}{self.entity_uri}/{service_plan_guid}/visibility") + return super()._get(f"{self.target_endpoint}{self.entity_uri}/{service_plan_guid}/visibility") # Updates a service plan visibility. It behaves similar to the POST service plan visibility endpoint but # this endpoint will REPLACE the existing list of organizations when the service plan is organization visible. @@ -32,7 +32,7 @@ def update_visibility(self, service_plan_guid: str, type: str, organizations: li payload = {"type": type} if organizations: payload["organizations"] = organizations - return super(ServicePlanManager, self)._patch( + return super()._patch( url=f"{self.target_endpoint}{self.entity_uri}/{service_plan_guid}/visibility", data=payload ) @@ -40,11 +40,11 @@ def update_visibility(self, service_plan_guid: str, type: str, organizations: li # this endpoint will APPEND to the existing list of organizations when the service plan is organization visible. def apply_visibility_to_extra_orgs(self, service_plan_guid: str, organizations: list[dict]) -> dict: payload = {"type": "organization", "organizations": organizations} - return super(ServicePlanManager, self)._post( + return super()._post( url=f"{self.target_endpoint}{self.entity_uri}/{service_plan_guid}/visibility", data=payload, files=None ) def remove_org_from_service_plan_visibility(self, service_plan_guid: str, org_guid: str): - super(ServicePlanManager, self)._delete( + super()._delete( url=f"{self.target_endpoint}{self.entity_uri}/{service_plan_guid}/visibility/{org_guid}" ) diff --git a/cloudfoundry_client/v3/spaces.py b/cloudfoundry_client/v3/spaces.py index ec5751e..765221c 100644 --- a/cloudfoundry_client/v3/spaces.py +++ b/cloudfoundry_client/v3/spaces.py @@ -8,17 +8,17 @@ class SpaceManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(SpaceManager, self).__init__(target_endpoint, client, "/v3/spaces") + super().__init__(target_endpoint, client, "/v3/spaces") def create(self, name: str, org_guid: str) -> Entity: - return super(SpaceManager, self)._create(dict(name=name, relationships=dict(organization=ToOneRelationship(org_guid)))) + return super()._create(dict(name=name, relationships=dict(organization=ToOneRelationship(org_guid)))) def update(self, space_guid: str, name: str) -> Entity: - return super(SpaceManager, self)._update(space_guid, dict(name=name)) + return super()._update(space_guid, dict(name=name)) def get_assigned_isolation_segment(self, space_guid: str) -> ToOneRelationship: return ToOneRelationship.from_json_object( - super(SpaceManager, self)._get( + super()._get( "%s%s/%s/relationships/isolation_segment" % (self.target_endpoint, self.entity_uri, space_guid) ) ) diff --git a/cloudfoundry_client/v3/tasks.py b/cloudfoundry_client/v3/tasks.py index 0cda445..a7a0112 100644 --- a/cloudfoundry_client/v3/tasks.py +++ b/cloudfoundry_client/v3/tasks.py @@ -8,7 +8,7 @@ class TaskManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(TaskManager, self).__init__(target_endpoint, client, "/v3/tasks") + super().__init__(target_endpoint, client, "/v3/tasks") def create( self, diff --git a/tests/abstract_test_case.py b/tests/abstract_test_case.py index b3c737f..46ec190 100644 --- a/tests/abstract_test_case.py +++ b/tests/abstract_test_case.py @@ -15,7 +15,7 @@ def mock_cloudfoundry_client_class(): class MockClass(CredentialManager): def __init__(self, *args, **kwargs): - super(MockClass, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) for attribute in mocked_attributes: setattr(self, attribute, MagicMock()) From 9a573a598219f75a90e3178be4860cac759ab657 Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Sat, 10 Jan 2026 14:39:38 +0100 Subject: [PATCH 244/264] chore(code) remove remaining super() --- cloudfoundry_client/client.py | 14 +++++++------- cloudfoundry_client/networking/entities.py | 2 +- .../networking/v1/external/policies.py | 6 +++--- cloudfoundry_client/v2/apps.py | 2 +- cloudfoundry_client/v3/apps.py | 12 ++++++------ cloudfoundry_client/v3/droplets.py | 8 ++++---- cloudfoundry_client/v3/entities.py | 8 ++++---- cloudfoundry_client/v3/isolation_segments.py | 4 ++-- cloudfoundry_client/v3/organizations.py | 10 +++++----- cloudfoundry_client/v3/packages.py | 6 +++--- cloudfoundry_client/v3/routes.py | 8 ++++---- cloudfoundry_client/v3/spaces.py | 4 ++-- cloudfoundry_client/v3/stacks.py | 10 +++++----- cloudfoundry_client/v3/users.py | 8 ++++---- 14 files changed, 51 insertions(+), 51 deletions(-) diff --git a/cloudfoundry_client/client.py b/cloudfoundry_client/client.py index 9046c2c..f78180b 100644 --- a/cloudfoundry_client/client.py +++ b/cloudfoundry_client/client.py @@ -321,7 +321,7 @@ def __getattr__(self, item): raise AttributeError("type '%s' has no attribute '%s'" % (type(self).__name__, item)) def _grant_password_request(self, login: str, password: str) -> dict: - request = super(CloudFoundryClient, self)._grant_password_request(login, password) + request = super()._grant_password_request(login, password) if self.token_format is not None: request["token_format"] = self.token_format if self.login_hint is not None: @@ -329,7 +329,7 @@ def _grant_password_request(self, login: str, password: str) -> dict: return request def _grant_refresh_token_request(self, refresh_token: str) -> dict: - request = super(CloudFoundryClient, self)._grant_refresh_token_request(refresh_token) + request = super()._grant_refresh_token_request(refresh_token) if self.token_format is not None: request["token_format"] = self.token_format return request @@ -343,27 +343,27 @@ def _grant_client_credentials_request(self) -> dict: ) def get(self, url: str, params: dict | None = None, **kwargs) -> Response: - response = super(CloudFoundryClient, self).get(url, params, **kwargs) + response = super().get(url, params, **kwargs) CloudFoundryClient._log_request("GET", url, response) return CloudFoundryClient._check_response(response) def post(self, url: str, data=None, json=None, **kwargs) -> Response: - response = super(CloudFoundryClient, self).post(url, data, json, **kwargs) + response = super().post(url, data, json, **kwargs) CloudFoundryClient._log_request("POST", url, response) return CloudFoundryClient._check_response(response) def put(self, url: str, data=None, json=None, **kwargs) -> Response: - response = super(CloudFoundryClient, self).put(url, data, json, **kwargs) + response = super().put(url, data, json, **kwargs) CloudFoundryClient._log_request("PUT", url, response) return CloudFoundryClient._check_response(response) def patch(self, url: str, data=None, json=None, **kwargs) -> Response: - response = super(CloudFoundryClient, self).patch(url, data, json, **kwargs) + response = super().patch(url, data, json, **kwargs) CloudFoundryClient._log_request("PATCH", url, response) return CloudFoundryClient._check_response(response) def delete(self, url: str, **kwargs) -> Response: - response = super(CloudFoundryClient, self).delete(url, **kwargs) + response = super().delete(url, **kwargs) CloudFoundryClient._log_request("DELETE", url, response) return CloudFoundryClient._check_response(response) diff --git a/cloudfoundry_client/networking/entities.py b/cloudfoundry_client/networking/entities.py index e28c06f..aebb2e7 100644 --- a/cloudfoundry_client/networking/entities.py +++ b/cloudfoundry_client/networking/entities.py @@ -17,7 +17,7 @@ class Entity(JsonObject): def __init__(self, target_endpoint: str, client: "CloudFoundryClient", *args, **kwargs): - super(Entity, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.target_endpoint = target_endpoint self.client = client try: diff --git a/cloudfoundry_client/networking/v1/external/policies.py b/cloudfoundry_client/networking/v1/external/policies.py index 826093f..e6afd1f 100644 --- a/cloudfoundry_client/networking/v1/external/policies.py +++ b/cloudfoundry_client/networking/v1/external/policies.py @@ -42,7 +42,7 @@ def dump(self): class PolicyManager(EntityManager): def __init__(self, target_endpoint, client): - super(PolicyManager, self).__init__(target_endpoint, client, "/networking/v1/external/policies") + super().__init__(target_endpoint, client, "/networking/v1/external/policies") def create(self, policies: list[Policy]): """create a new network policy @@ -59,7 +59,7 @@ def create(self, policies: list[Policy]): if not isinstance(policy, Policy): raise TypeError data.append(policy.dump()) - return super(PolicyManager, self)._create({"policies": data}) + return super()._create({"policies": data}) def delete(self, policies: list[Policy]): """remove a new network policy @@ -76,4 +76,4 @@ def delete(self, policies: list[Policy]): if not isinstance(policy, Policy): raise TypeError data.append(policy.dump()) - return super(PolicyManager, self)._delete({"policies": data}) + return super()._delete({"policies": data}) diff --git a/cloudfoundry_client/v2/apps.py b/cloudfoundry_client/v2/apps.py index bd78961..26b36c6 100644 --- a/cloudfoundry_client/v2/apps.py +++ b/cloudfoundry_client/v2/apps.py @@ -150,7 +150,7 @@ def update(self, application_guid: str, **kwargs) -> Application: return super()._update(application_guid, request) def remove(self, application_guid: str): - super(AppManager, self)._remove(application_guid) + super()._remove(application_guid) def upload(self, application_guid: str, resources, application: str, asynchronous: bool | None = False): application_size = os.path.getsize(application) diff --git a/cloudfoundry_client/v3/apps.py b/cloudfoundry_client/v3/apps.py index 6b309d1..c3cc609 100644 --- a/cloudfoundry_client/v3/apps.py +++ b/cloudfoundry_client/v3/apps.py @@ -18,27 +18,27 @@ def remove(self, application_guid: str, asynchronous: bool = True) -> str | None return super()._remove(application_guid, asynchronous) def get_env(self, application_guid: str) -> JsonObject: - return super(AppManager, self)._get("%s%s/%s/env" % (self.target_endpoint, self.entity_uri, application_guid)) + return super()._get("%s%s/%s/env" % (self.target_endpoint, self.entity_uri, application_guid)) def list_routes(self, application_guid: str, **kwargs) -> Pagination[Entity]: uri: str = "%s/%s/routes" % (self.entity_uri, application_guid) - return super(AppManager, self)._list(requested_path=uri, **kwargs) + return super()._list(requested_path=uri, **kwargs) def list_droplets(self, application_guid: str, **kwargs) -> Pagination[Entity]: uri: str = "%s/%s/droplets" % (self.entity_uri, application_guid) - return super(AppManager, self)._list(requested_path=uri, **kwargs) + return super()._list(requested_path=uri, **kwargs) def get_manifest(self, application_guid: str) -> str: return self.client.get(url="%s%s/%s/manifest" % (self.target_endpoint, self.entity_uri, application_guid)).text def list_packages(self, application_guid: str, **kwargs) -> Pagination[Entity]: uri: str = "%s/%s/packages" % (self.entity_uri, application_guid) - return super(AppManager, self)._list(requested_path=uri, **kwargs) + return super()._list(requested_path=uri, **kwargs) def list_revisions(self, application_guid: str, **kwargs) -> Pagination[Entity]: uri: str = "%s/%s/revisions" % (self.entity_uri, application_guid) - return super(AppManager, self)._list(requested_path=uri, **kwargs) + return super()._list(requested_path=uri, **kwargs) def list_deployed_revisions(self, application_guid: str, **kwargs) -> Pagination[Entity]: uri: str = "%s/%s/revisions/deployed" % (self.entity_uri, application_guid) - return super(AppManager, self)._list(requested_path=uri, **kwargs) + return super()._list(requested_path=uri, **kwargs) diff --git a/cloudfoundry_client/v3/droplets.py b/cloudfoundry_client/v3/droplets.py index 7f4bb69..7683672 100644 --- a/cloudfoundry_client/v3/droplets.py +++ b/cloudfoundry_client/v3/droplets.py @@ -8,7 +8,7 @@ class DropletManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(DropletManager, self).__init__(target_endpoint, client, "/v3/droplets") + super().__init__(target_endpoint, client, "/v3/droplets") def create(self, app_guid: str, @@ -24,7 +24,7 @@ def create(self, if process_types is not None: data["process_types"] = process_types self._metadata(data, meta_labels, meta_annotations) - return super(DropletManager, self)._create(data) + return super()._create(data) def copy(self, droplet_guid: str, @@ -51,7 +51,7 @@ def update(self, ) -> Entity: data: dict[str, Any] = {} self._metadata(data, meta_labels, meta_annotations) - return super(DropletManager, self)._update(droplet_gid, data) + return super()._update(droplet_gid, data) def remove(self, route_gid: str): - return super(DropletManager, self)._remove(route_gid) + return super()._remove(route_gid) diff --git a/cloudfoundry_client/v3/entities.py b/cloudfoundry_client/v3/entities.py index e7924ac..603d801 100644 --- a/cloudfoundry_client/v3/entities.py +++ b/cloudfoundry_client/v3/entities.py @@ -19,7 +19,7 @@ def plural(name: str) -> str: class Entity(JsonObject): def __init__(self, target_endpoint: str, client: "CloudFoundryClient", **kwargs): - super(Entity, self).__init__(**kwargs) + super().__init__(**kwargs) default_manager = self._default_manager(target_endpoint, client) self._create_navigable_links(client.v3, default_manager) self._create_navigable_included_entities(client.v3, default_manager) @@ -81,7 +81,7 @@ def _manager_method(link_name: str, link_method: str) -> str | None: class Relationship(JsonObject): def __init__(self, guid: str | None): - super(Relationship, self).__init__(guid=guid) + super().__init__(guid=guid) class ToOneRelationship(JsonObject): @@ -95,7 +95,7 @@ def from_json_object(to_one_relationship: JsonObject): return result def __init__(self, guid: str | None): - super(ToOneRelationship, self).__init__(data=Relationship(guid)) + super().__init__(data=Relationship(guid)) self.guid = guid @@ -107,7 +107,7 @@ def from_json_object(to_many_relations: JsonObject): return result def __init__(self, *guids: str): - super(ToManyRelationship, self).__init__(data=[Relationship(guid) for guid in guids]) + super().__init__(data=[Relationship(guid) for guid in guids]) self.guids = list(guids) diff --git a/cloudfoundry_client/v3/isolation_segments.py b/cloudfoundry_client/v3/isolation_segments.py index a257b6f..158772e 100644 --- a/cloudfoundry_client/v3/isolation_segments.py +++ b/cloudfoundry_client/v3/isolation_segments.py @@ -51,9 +51,9 @@ def list_entitled_spaces( ) def revoke_organization(self, isolation_segment_guid: str, org_guid: str): - super(IsolationSegmentManager, self)._delete( + super()._delete( "%s%s/%s/relationships/organizations/%s" % (self.target_endpoint, self.entity_uri, isolation_segment_guid, org_guid) ) def remove(self, isolation_segment_guid: str): - super(IsolationSegmentManager, self)._remove(isolation_segment_guid) + super()._remove(isolation_segment_guid) diff --git a/cloudfoundry_client/v3/organizations.py b/cloudfoundry_client/v3/organizations.py index 2a325e8..2c889e6 100644 --- a/cloudfoundry_client/v3/organizations.py +++ b/cloudfoundry_client/v3/organizations.py @@ -32,11 +32,11 @@ def update( return super()._update(guid, data) def remove(self, guid: str, asynchronous: bool = True) -> str | None: - return super(OrganizationManager, self)._remove(guid, asynchronous) + return super()._remove(guid, asynchronous) def assign_default_isolation_segment(self, org_guid: str, iso_seg_guid: str) -> Entity: return ToOneRelationship.from_json_object( - super(OrganizationManager, self)._patch( + super()._patch( "%s%s/%s/relationships/default_isolation_segment" % (self.target_endpoint, self.entity_uri, org_guid), data=ToOneRelationship(iso_seg_guid), ) @@ -44,11 +44,11 @@ def assign_default_isolation_segment(self, org_guid: str, iso_seg_guid: str) -> def get_default_isolation_segment(self, guid: str) -> ToOneRelationship: return ToOneRelationship.from_json_object( - super(OrganizationManager, self).get(guid, "relationships", "default_isolation_segment") + super().get(guid, "relationships", "default_isolation_segment") ) def get_default_domain(self, guid: str) -> Entity: - return super(OrganizationManager, self).get(guid, "domains", "default") + return super().get(guid, "domains", "default") def get_usage_summary(self, guid: str) -> Entity: - return super(OrganizationManager, self).get(guid, "usage_summary") + return super().get(guid, "usage_summary") diff --git a/cloudfoundry_client/v3/packages.py b/cloudfoundry_client/v3/packages.py index cb554dd..c45ea0a 100644 --- a/cloudfoundry_client/v3/packages.py +++ b/cloudfoundry_client/v3/packages.py @@ -15,7 +15,7 @@ class PackageType(Enum): class PackageManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(PackageManager, self).__init__(target_endpoint, client, "/v3/packages") + super().__init__(target_endpoint, client, "/v3/packages") def create(self, app_guid: str, @@ -30,7 +30,7 @@ def create(self, }, } self._metadata(data, meta_labels, meta_annotations) - return super(PackageManager, self)._create(data) + return super()._create(data) def copy(self, package_guid: str, @@ -52,4 +52,4 @@ def copy(self, def list_droplets(self, package_guid: str, **kwargs) -> Pagination[Entity]: uri: str = "%s/%s/droplets" % (self.entity_uri, package_guid) - return super(PackageManager, self)._list(requested_path=uri, **kwargs) + return super()._list(requested_path=uri, **kwargs) diff --git a/cloudfoundry_client/v3/routes.py b/cloudfoundry_client/v3/routes.py index 4063971..3265d5f 100644 --- a/cloudfoundry_client/v3/routes.py +++ b/cloudfoundry_client/v3/routes.py @@ -14,7 +14,7 @@ class LoadBalancing(Enum): class RouteManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(RouteManager, self).__init__(target_endpoint, client, "/v3/routes") + super().__init__(target_endpoint, client, "/v3/routes") def create(self, space_guid: str, @@ -40,7 +40,7 @@ def create(self, if load_balancing is not None: data["options"] = {"loadbalancing": load_balancing.value} self._metadata(data, meta_labels, meta_annotations) - return super(RouteManager, self)._create(data) + return super()._create(data) def update(self, route_gid: str, @@ -52,7 +52,7 @@ def update(self, if load_balancing is not None: data["options"] = {"loadbalancing": load_balancing.value} self._metadata(data, meta_labels, meta_annotations) - return super(RouteManager, self)._update(route_gid, data) + return super()._update(route_gid, data) def remove(self, route_gid: str): - return super(RouteManager, self)._remove(route_gid) + return super()._remove(route_gid) diff --git a/cloudfoundry_client/v3/spaces.py b/cloudfoundry_client/v3/spaces.py index 765221c..42f5a7d 100644 --- a/cloudfoundry_client/v3/spaces.py +++ b/cloudfoundry_client/v3/spaces.py @@ -25,11 +25,11 @@ def get_assigned_isolation_segment(self, space_guid: str) -> ToOneRelationship: def assign_isolation_segment(self, space_guid: str, isolation_segment_guid: str | None) -> ToOneRelationship: return ToOneRelationship.from_json_object( - super(SpaceManager, self)._patch( + super()._patch( "%s%s/%s/relationships/isolation_segment" % (self.target_endpoint, self.entity_uri, space_guid), dict(data=None) if isolation_segment_guid is None else ToOneRelationship(isolation_segment_guid), ) ) def remove(self, space_guid: str): - super(SpaceManager, self)._remove(space_guid) + super()._remove(space_guid) diff --git a/cloudfoundry_client/v3/stacks.py b/cloudfoundry_client/v3/stacks.py index 6004f2e..751561f 100644 --- a/cloudfoundry_client/v3/stacks.py +++ b/cloudfoundry_client/v3/stacks.py @@ -9,7 +9,7 @@ class StackMananager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(StackMananager, self).__init__(target_endpoint, client, "/v3/stacks") + super().__init__(target_endpoint, client, "/v3/stacks") def create( self, @@ -22,7 +22,7 @@ def create( if description is not None: data["description"] = description self._metadata(data, meta_labels, meta_annotations) - return super(StackMananager, self)._create(data) + return super()._create(data) def update( self, @@ -32,11 +32,11 @@ def update( ) -> Entity: data = {} self._metadata(data, meta_labels, meta_annotations) - return super(StackMananager, self)._update(stack_guid, data) + return super()._update(stack_guid, data) def list_apps(self, stack_guid: str, **kwargs) -> Pagination[Entity]: uri: str = "%s/%s/apps" % (self.entity_uri, stack_guid) - return super(StackMananager, self)._list(requested_path=uri, **kwargs) + return super()._list(requested_path=uri, **kwargs) def remove(self, stack_guid: str): - super(StackMananager, self)._remove(stack_guid) + super()._remove(stack_guid) diff --git a/cloudfoundry_client/v3/users.py b/cloudfoundry_client/v3/users.py index cadc48f..6fad9f1 100644 --- a/cloudfoundry_client/v3/users.py +++ b/cloudfoundry_client/v3/users.py @@ -8,7 +8,7 @@ class UserManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): - super(UserManager, self).__init__(target_endpoint, client, "/v3/users") + super().__init__(target_endpoint, client, "/v3/users") def create( self, @@ -24,7 +24,7 @@ def create( data["username"] = username data["origin"] = origin self._metadata(data, meta_labels, meta_annotations) - return super(UserManager, self)._create(data) + return super()._create(data) def update( self, @@ -34,7 +34,7 @@ def update( ) -> Entity: data = {} self._metadata(data, meta_labels, meta_annotations) - return super(UserManager, self)._update(guid, data) + return super()._update(guid, data) def remove(self, guid: str) -> str | None: - return super(UserManager, self)._remove(guid) + return super()._remove(guid) From 2bbf0b88fae72dd7dcbdfd1c0720b7f12cf54230 Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Sat, 10 Jan 2026 14:49:32 +0100 Subject: [PATCH 245/264] feature(v3) add audit event manager (#289) See #280 --- cloudfoundry_client/client.py | 2 + cloudfoundry_client/v3/audit_events.py | 11 +++++ .../v3/audit_events/GET_response.json | 48 +++++++++++++++++++ .../v3/audit_events/GET_{id}_response.json | 32 +++++++++++++ tests/v3/test_audit_events.py | 39 +++++++++++++++ 5 files changed, 132 insertions(+) create mode 100644 cloudfoundry_client/v3/audit_events.py create mode 100644 tests/fixtures/v3/audit_events/GET_response.json create mode 100644 tests/fixtures/v3/audit_events/GET_{id}_response.json create mode 100644 tests/v3/test_audit_events.py diff --git a/cloudfoundry_client/client.py b/cloudfoundry_client/client.py index f78180b..a4ecfe7 100644 --- a/cloudfoundry_client/client.py +++ b/cloudfoundry_client/client.py @@ -28,6 +28,7 @@ from cloudfoundry_client.v2.spaces import SpaceManager as SpaceManagerV2 from cloudfoundry_client.v3.apps import AppManager +from cloudfoundry_client.v3.audit_events import AuditEventManager from cloudfoundry_client.v3.buildpacks import BuildpackManager from cloudfoundry_client.v3.domains import DomainManager from cloudfoundry_client.v3.droplets import DropletManager @@ -119,6 +120,7 @@ class V3(object): def __init__(self, cloud_controller_v3_url: str, credential_manager: "CloudFoundryClient"): target_endpoint = cloud_controller_v3_url.removesuffix("/v3") self.apps = AppManager(target_endpoint, credential_manager) + self.audit_events = AuditEventManager(target_endpoint, credential_manager) self.buildpacks = BuildpackManager(target_endpoint, credential_manager) self.domains = DomainManager(target_endpoint, credential_manager) self.droplets = DropletManager(target_endpoint, credential_manager) diff --git a/cloudfoundry_client/v3/audit_events.py b/cloudfoundry_client/v3/audit_events.py new file mode 100644 index 0000000..aef9758 --- /dev/null +++ b/cloudfoundry_client/v3/audit_events.py @@ -0,0 +1,11 @@ +from typing import TYPE_CHECKING + +from cloudfoundry_client.v3.entities import EntityManager, Entity + +if TYPE_CHECKING: + from cloudfoundry_client.client import CloudFoundryClient + + +class AuditEventManager(EntityManager[Entity]): + def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): + super().__init__(target_endpoint, client, "/v3/audit_events") diff --git a/tests/fixtures/v3/audit_events/GET_response.json b/tests/fixtures/v3/audit_events/GET_response.json new file mode 100644 index 0000000..23a01aa --- /dev/null +++ b/tests/fixtures/v3/audit_events/GET_response.json @@ -0,0 +1,48 @@ +{ + "pagination": { + "total_results": 1, + "total_pages": 1, + "first": { + "href": "https://api.example.org/v3/audit_events?page=1&per_page=2" + }, + "last": { + "href": "https://api.example.org/v3/audit_events?page=1&per_page=2" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "a595fe2f-01ff-4965-a50c-290258ab8582", + "created_at": "2016-06-08T16:41:23Z", + "updated_at": "2016-06-08T16:41:26Z", + "type": "audit.app.update", + "actor": { + "guid": "d144abe3-3d7b-40d4-b63f-2584798d3ee5", + "type": "user", + "name": "admin" + }, + "target": { + "guid": "2e3151ba-9a63-4345-9c5b-6d8c238f4e55", + "type": "app", + "name": "my-app" + }, + "data": { + "request": { + "recursive": true + } + }, + "space": { + "guid": "cb97dd25-d4f7-4185-9e6f-ad6e585c207c" + }, + "organization": { + "guid": "d9be96f5-ea8f-4549-923f-bec882e32e3c" + }, + "links": { + "self": { + "href": "https://api.example.org//v3/audit_events/a595fe2f-01ff-4965-a50c-290258ab8582" + } + } + } + ] +} diff --git a/tests/fixtures/v3/audit_events/GET_{id}_response.json b/tests/fixtures/v3/audit_events/GET_{id}_response.json new file mode 100644 index 0000000..58b9e1a --- /dev/null +++ b/tests/fixtures/v3/audit_events/GET_{id}_response.json @@ -0,0 +1,32 @@ +{ + "guid": "a595fe2f-01ff-4965-a50c-290258ab8582", + "created_at": "2016-06-08T16:41:23Z", + "updated_at": "2016-06-08T16:41:26Z", + "type": "audit.app.update", + "actor": { + "guid": "d144abe3-3d7b-40d4-b63f-2584798d3ee5", + "type": "user", + "name": "admin" + }, + "target": { + "guid": "2e3151ba-9a63-4345-9c5b-6d8c238f4e55", + "type": "app", + "name": "my-app" + }, + "data": { + "request": { + "recursive": true + } + }, + "space": { + "guid": "cb97dd25-d4f7-4185-9e6f-ad6e585c207c" + }, + "organization": { + "guid": "d9be96f5-ea8f-4549-923f-bec882e32e3c" + }, + "links": { + "self": { + "href": "https://api.example.org/v3/audit_events/a595fe2f-01ff-4965-a50c-290258ab8582" + } + } +} diff --git a/tests/v3/test_audit_events.py b/tests/v3/test_audit_events.py new file mode 100644 index 0000000..239812b --- /dev/null +++ b/tests/v3/test_audit_events.py @@ -0,0 +1,39 @@ +import unittest +from http import HTTPStatus + +from abstract_test_case import AbstractTestCase +from cloudfoundry_client.v3.entities import Entity + + +class TestAuditEvents(unittest.TestCase, AbstractTestCase): + @classmethod + def setUpClass(cls): + cls.mock_client_class() + + def setUp(self): + self.build_client() + + def test_list(self): + self.client.get.return_value = self.mock_response( + "/v3/audit_events", + HTTPStatus.OK, + None, + "v3", "audit_events", "GET_response.json" + ) + all_audit_events = [audit_event for audit_event in self.client.v3.audit_events.list()] + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertEqual(1, len(all_audit_events)) + self.assertEqual(all_audit_events[0]["type"], "audit.app.update") + self.assertIsInstance(all_audit_events[0], Entity) + + def test_get(self): + self.client.get.return_value = self.mock_response( + "/v3/audit_events/audit-event-id", + HTTPStatus.OK, + None, + "v3", "audit_events", "GET_{id}_response.json" + ) + result = self.client.v3.audit_events.get("audit-event-id") + self.client.get.assert_called_with(self.client.get.return_value.url) + self.assertIsNotNone(result) + self.assertIsInstance(result, Entity) From 9b3a5de7008bcebcb23dfe707abe0ab91fa2a7c9 Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Sat, 10 Jan 2026 14:53:49 +0100 Subject: [PATCH 246/264] chore(code) small polish on organization quotas --- cloudfoundry_client/v3/organization_quotas.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cloudfoundry_client/v3/organization_quotas.py b/cloudfoundry_client/v3/organization_quotas.py index 9304630..5078c44 100644 --- a/cloudfoundry_client/v3/organization_quotas.py +++ b/cloudfoundry_client/v3/organization_quotas.py @@ -37,9 +37,6 @@ class OrganizationQuotaManager(EntityManager[Entity]): def __init__(self, target_endpoint: str, client: "CloudFoundryClient"): super().__init__(target_endpoint, client, "/v3/organization_quotas") - def remove(self, guid: str, asynchronous: bool = True) -> str | None: - return super()._remove(guid, asynchronous) - def create( self, name: str, @@ -71,6 +68,9 @@ def apply_to_organizations(self, guid: str, organizations: ToManyRelationship) - ) ) + def remove(self, guid: str, asynchronous: bool = True) -> str | None: + return super()._remove(guid, asynchronous) + def _asdict( self, name: str, From 158193a45883badb402131bbcaeae1695115fc96 Mon Sep 17 00:00:00 2001 From: David Duarte <102606398+daviddmd@users.noreply.github.com> Date: Wed, 14 Jan 2026 17:06:18 +0100 Subject: [PATCH 247/264] feat: Add function to list organization domains (#290) --- cloudfoundry_client/v3/organizations.py | 5 ++ .../GET_{id}_domains_response.json | 48 +++++++++++++++++++ tests/v3/test_organizations.py | 21 ++++++++ 3 files changed, 74 insertions(+) create mode 100644 tests/fixtures/v3/organizations/GET_{id}_domains_response.json diff --git a/cloudfoundry_client/v3/organizations.py b/cloudfoundry_client/v3/organizations.py index 2c889e6..2b22cc2 100644 --- a/cloudfoundry_client/v3/organizations.py +++ b/cloudfoundry_client/v3/organizations.py @@ -1,5 +1,6 @@ from typing import TYPE_CHECKING +from cloudfoundry_client.common_objects import Pagination from cloudfoundry_client.v3.entities import EntityManager, Entity, ToOneRelationship if TYPE_CHECKING: @@ -47,6 +48,10 @@ def get_default_isolation_segment(self, guid: str) -> ToOneRelationship: super().get(guid, "relationships", "default_isolation_segment") ) + def list_domains(self, guid: str, **kwargs) -> Pagination[Entity]: + uri: str = "%s/%s/domains" % (self.entity_uri, guid) + return super()._list(requested_path=uri, **kwargs) + def get_default_domain(self, guid: str) -> Entity: return super().get(guid, "domains", "default") diff --git a/tests/fixtures/v3/organizations/GET_{id}_domains_response.json b/tests/fixtures/v3/organizations/GET_{id}_domains_response.json new file mode 100644 index 0000000..92e6fd4 --- /dev/null +++ b/tests/fixtures/v3/organizations/GET_{id}_domains_response.json @@ -0,0 +1,48 @@ +{ + "pagination": { + "total_results": 1, + "total_pages": 1, + "first": { + "href": "https://api.example.org/v3/organizations/016b770b-b447-4a12-800a-1b4c69406a9f/domains?page=1&per_page=50" + }, + "last": { + "href": "https://api.example.org/v3/organizations/016b770b-b447-4a12-800a-1b4c69406a9f/domains?page=1&per_page=50" + }, + "next": null, + "previous": null + }, + "resources": [ + { + "guid": "3a5d3d89-3f89-4f05-8188-8a2b298c79d5", + "created_at": "2019-03-08T01:06:19Z", + "updated_at": "2019-03-08T01:06:19Z", + "name": "test-domain.com", + "internal": false, + "router_group": { "guid": "5806148f-cce6-4d86-7fbd-aa269e3f6f3f" }, + "supported_protocols": ["tcp"], + "metadata": { + "labels": {}, + "annotations": {} + }, + "relationships": { + "organization": { + "data": null + }, + "shared_organizations": { + "data": [] + } + }, + "links": { + "self": { + "href": "https://api.example.org/v3/domains/016b770b-b447-4a12-800a-1b4c69406a9f" + }, + "route_reservations": { + "href": "https://api.example.org/v3/domains/016b770b-b447-4a12-800a-1b4c69406a9f/route_reservations" + }, + "router_group": { + "href": "https://api.example.org/routing/v1/router_groups/5806148f-cce6-4d86-7fbd-aa269e3f6f3f" + } + } + } + ] +} \ No newline at end of file diff --git a/tests/v3/test_organizations.py b/tests/v3/test_organizations.py index e8c3f73..e1b90dd 100644 --- a/tests/v3/test_organizations.py +++ b/tests/v3/test_organizations.py @@ -5,6 +5,8 @@ import cloudfoundry_client.main.main as main from abstract_test_case import AbstractTestCase + +from cloudfoundry_client.common_objects import Pagination from cloudfoundry_client.v3.entities import Entity, ToOneRelationship @@ -144,6 +146,25 @@ def test_get_usage_summary(self): self.client.v3.organizations.get_usage_summary("organization_id") self.client.get.assert_called_with(self.client.get.return_value.url) + def test_get_domains(self): + self.client.get.return_value = self.mock_response( + "/v3/organizations/organization_id/domains", + HTTPStatus.OK, + {"Content-Type": "application/json"}, + "v3", + "organizations", + "GET_{id}_domains_response.json", + ) + organization_domains_response: Pagination[Entity] = self.client.v3.organizations.list_domains("organization_id") + domains: list[dict] = [domain for domain in organization_domains_response] + print(domains) + self.assertIsInstance(domains, list) + self.assertEqual(len(domains), 1) + domain: dict = domains[0] + self.assertIsInstance(domain, dict) + self.assertEqual(domain.get("guid"), "3a5d3d89-3f89-4f05-8188-8a2b298c79d5") + self.assertEqual(domain.get("name"), "test-domain.com") + @patch.object(sys, "argv", ["main", "list_organizations"]) def test_main_list_organizations(self): with patch("cloudfoundry_client.main.main.build_client_from_configuration", new=lambda: self.client): From d28211a6f2c6b25e030bb674c2af4d92c589159f Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Sat, 17 Jan 2026 17:14:53 +0100 Subject: [PATCH 248/264] prepare 1.40.1 --- cloudfoundry_client/__init__.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudfoundry_client/__init__.py b/cloudfoundry_client/__init__.py index b3daaa3..97a568b 100644 --- a/cloudfoundry_client/__init__.py +++ b/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.39.2" +__version__ = "1.40.1" diff --git a/pyproject.toml b/pyproject.toml index e666238..aacd1b2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ exclude = ''' [tool.poetry] name = "cloudfoundry_client" -version = "1.39.2" +version = "1.40.1" description = "A client library for CloudFoundry" authors = ["Benjamin Einaudi "] readme = "README.rst" From 7f4607e6cc4c7974931b2c992e89a391000bebec Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Wed, 21 Jan 2026 19:24:22 +0100 Subject: [PATCH 249/264] fixes(v3) put back 'get_routes' un AppManager (#292) Fixes #291 --- cloudfoundry_client/__init__.py | 2 +- cloudfoundry_client/v3/apps.py | 11 +++++++++++ pyproject.toml | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/cloudfoundry_client/__init__.py b/cloudfoundry_client/__init__.py index 97a568b..970ecdd 100644 --- a/cloudfoundry_client/__init__.py +++ b/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.40.1" +__version__ = "1.40.2" diff --git a/cloudfoundry_client/v3/apps.py b/cloudfoundry_client/v3/apps.py index c3cc609..7054434 100644 --- a/cloudfoundry_client/v3/apps.py +++ b/cloudfoundry_client/v3/apps.py @@ -1,4 +1,10 @@ from typing import TYPE_CHECKING +import sys + +if sys.version_info < (3, 13): + from typing_extensions import deprecated +else: + from warnings import deprecated from cloudfoundry_client.common_objects import JsonObject, Pagination from cloudfoundry_client.v3.entities import EntityManager, Entity @@ -20,6 +26,11 @@ def remove(self, application_guid: str, asynchronous: bool = True) -> str | None def get_env(self, application_guid: str) -> JsonObject: return super()._get("%s%s/%s/env" % (self.target_endpoint, self.entity_uri, application_guid)) + @deprecated("use list_routes instead") + def get_routes(self, application_guid: str) -> JsonObject: + return super()._get( + "%s%s/%s/routes" % (self.target_endpoint, self.entity_uri, application_guid)) + def list_routes(self, application_guid: str, **kwargs) -> Pagination[Entity]: uri: str = "%s/%s/routes" % (self.entity_uri, application_guid) return super()._list(requested_path=uri, **kwargs) diff --git a/pyproject.toml b/pyproject.toml index aacd1b2..8edb7ae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ exclude = ''' [tool.poetry] name = "cloudfoundry_client" -version = "1.40.1" +version = "1.40.2" description = "A client library for CloudFoundry" authors = ["Benjamin Einaudi "] readme = "README.rst" From f87fd131a94bc9ea7ffb6bfeef06dd47b51c2901 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Feb 2026 09:55:24 +0100 Subject: [PATCH 250/264] chore(deps): bump protobuf from 6.33.2 to 6.33.5 (#293) Bumps [protobuf](https://github.com/protocolbuffers/protobuf) from 6.33.2 to 6.33.5. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Commits](https://github.com/protocolbuffers/protobuf/commits) --- updated-dependencies: - dependency-name: protobuf dependency-version: 6.33.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 24 ++++++++++++------------ pyproject.toml | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/poetry.lock b/poetry.lock index fe7ecaa..8f8f571 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1467,22 +1467,22 @@ files = [ [[package]] name = "protobuf" -version = "6.33.2" +version = "6.33.5" description = "" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "protobuf-6.33.2-cp310-abi3-win32.whl", hash = "sha256:87eb388bd2d0f78febd8f4c8779c79247b26a5befad525008e49a6955787ff3d"}, - {file = "protobuf-6.33.2-cp310-abi3-win_amd64.whl", hash = "sha256:fc2a0e8b05b180e5fc0dd1559fe8ebdae21a27e81ac77728fb6c42b12c7419b4"}, - {file = "protobuf-6.33.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d9b19771ca75935b3a4422957bc518b0cecb978b31d1dd12037b088f6bcc0e43"}, - {file = "protobuf-6.33.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:b5d3b5625192214066d99b2b605f5783483575656784de223f00a8d00754fc0e"}, - {file = "protobuf-6.33.2-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8cd7640aee0b7828b6d03ae518b5b4806fdfc1afe8de82f79c3454f8aef29872"}, - {file = "protobuf-6.33.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:1f8017c48c07ec5859106533b682260ba3d7c5567b1ca1f24297ce03384d1b4f"}, - {file = "protobuf-6.33.2-cp39-cp39-win32.whl", hash = "sha256:7109dcc38a680d033ffb8bf896727423528db9163be1b6a02d6a49606dcadbfe"}, - {file = "protobuf-6.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:2981c58f582f44b6b13173e12bb8656711189c2a70250845f264b877f00b1913"}, - {file = "protobuf-6.33.2-py3-none-any.whl", hash = "sha256:7636aad9bb01768870266de5dc009de2d1b936771b38a793f73cbbf279c91c5c"}, - {file = "protobuf-6.33.2.tar.gz", hash = "sha256:56dc370c91fbb8ac85bc13582c9e373569668a290aa2e66a590c2a0d35ddb9e4"}, + {file = "protobuf-6.33.5-cp310-abi3-win32.whl", hash = "sha256:d71b040839446bac0f4d162e758bea99c8251161dae9d0983a3b88dee345153b"}, + {file = "protobuf-6.33.5-cp310-abi3-win_amd64.whl", hash = "sha256:3093804752167bcab3998bec9f1048baae6e29505adaf1afd14a37bddede533c"}, + {file = "protobuf-6.33.5-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:a5cb85982d95d906df1e2210e58f8e4f1e3cdc088e52c921a041f9c9a0386de5"}, + {file = "protobuf-6.33.5-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:9b71e0281f36f179d00cbcb119cb19dec4d14a81393e5ea220f64b286173e190"}, + {file = "protobuf-6.33.5-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8afa18e1d6d20af15b417e728e9f60f3aa108ee76f23c3b2c07a2c3b546d3afd"}, + {file = "protobuf-6.33.5-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:cbf16ba3350fb7b889fca858fb215967792dc125b35c7976ca4818bee3521cf0"}, + {file = "protobuf-6.33.5-cp39-cp39-win32.whl", hash = "sha256:a3157e62729aafb8df6da2c03aa5c0937c7266c626ce11a278b6eb7963c4e37c"}, + {file = "protobuf-6.33.5-cp39-cp39-win_amd64.whl", hash = "sha256:8f04fa32763dcdb4973d537d6b54e615cc61108c7cb38fe59310c3192d29510a"}, + {file = "protobuf-6.33.5-py3-none-any.whl", hash = "sha256:69915a973dd0f60f31a08b8318b73eab2bd6a392c79184b3612226b0a3f8ec02"}, + {file = "protobuf-6.33.5.tar.gz", hash = "sha256:6ddcac2a081f8b7b9642c09406bc6a4290128fce5f471cddd165960bb9119e5c"}, ] [[package]] @@ -2076,4 +2076,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.10" -content-hash = "819adce351d38dc60317d81e7985f03e1f0ae1eecc1e647bfb38d297c4889202" +content-hash = "47b6dd96d689ecb027ffaf300f2c3410ee3f95c2c5c5ac9ac950d1e7d416bbb1" diff --git a/pyproject.toml b/pyproject.toml index 8edb7ae..b51e963 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ keywords = ["cloudfoundry", "cf"] [tool.poetry.dependencies] python = ">=3.10" aiohttp = ">=3.8.0" -protobuf = "6.33.2" +protobuf = "6.33.5" oauth2-client= "1.4.2" websocket-client= "~1.9.0" PyYAML = ">=6.0" From fb20718cd227402644c59f19f40aac995021578a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Feb 2026 09:59:50 +0100 Subject: [PATCH 251/264] chore(deps-dev): bump black from 25.11.0 to 26.1.0 (#294) Bumps [black](https://github.com/psf/black) from 25.11.0 to 26.1.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/25.11.0...26.1.0) --- updated-dependencies: - dependency-name: black dependency-version: 26.1.0 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 75 +++++++++++++++++++++++++++----------------------- pyproject.toml | 2 +- 2 files changed, 42 insertions(+), 35 deletions(-) diff --git a/poetry.lock b/poetry.lock index 8f8f571..a51fe5c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -215,45 +215,46 @@ testing = ["jaraco.test", "pytest (!=8.0.*)", "pytest (>=6,!=8.1.*)", "pytest-ch [[package]] name = "black" -version = "25.11.0" +version = "26.1.0" description = "The uncompromising code formatter." optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "black-25.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ec311e22458eec32a807f029b2646f661e6859c3f61bc6d9ffb67958779f392e"}, - {file = "black-25.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1032639c90208c15711334d681de2e24821af0575573db2810b0763bcd62e0f0"}, - {file = "black-25.11.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0c0f7c461df55cf32929b002335883946a4893d759f2df343389c4396f3b6b37"}, - {file = "black-25.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:f9786c24d8e9bd5f20dc7a7f0cdd742644656987f6ea6947629306f937726c03"}, - {file = "black-25.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:895571922a35434a9d8ca67ef926da6bc9ad464522a5fe0db99b394ef1c0675a"}, - {file = "black-25.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cb4f4b65d717062191bdec8e4a442539a8ea065e6af1c4f4d36f0cdb5f71e170"}, - {file = "black-25.11.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d81a44cbc7e4f73a9d6ae449ec2317ad81512d1e7dce7d57f6333fd6259737bc"}, - {file = "black-25.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:7eebd4744dfe92ef1ee349dc532defbf012a88b087bb7ddd688ff59a447b080e"}, - {file = "black-25.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:80e7486ad3535636657aa180ad32a7d67d7c273a80e12f1b4bfa0823d54e8fac"}, - {file = "black-25.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6cced12b747c4c76bc09b4db057c319d8545307266f41aaee665540bc0e04e96"}, - {file = "black-25.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cb2d54a39e0ef021d6c5eef442e10fd71fcb491be6413d083a320ee768329dd"}, - {file = "black-25.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae263af2f496940438e5be1a0c1020e13b09154f3af4df0835ea7f9fe7bfa409"}, - {file = "black-25.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0a1d40348b6621cc20d3d7530a5b8d67e9714906dfd7346338249ad9c6cedf2b"}, - {file = "black-25.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:51c65d7d60bb25429ea2bf0731c32b2a2442eb4bd3b2afcb47830f0b13e58bfd"}, - {file = "black-25.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:936c4dd07669269f40b497440159a221ee435e3fddcf668e0c05244a9be71993"}, - {file = "black-25.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:f42c0ea7f59994490f4dccd64e6b2dd49ac57c7c84f38b8faab50f8759db245c"}, - {file = "black-25.11.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:35690a383f22dd3e468c85dc4b915217f87667ad9cce781d7b42678ce63c4170"}, - {file = "black-25.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dae49ef7369c6caa1a1833fd5efb7c3024bb7e4499bf64833f65ad27791b1545"}, - {file = "black-25.11.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bd4a22a0b37401c8e492e994bce79e614f91b14d9ea911f44f36e262195fdda"}, - {file = "black-25.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:aa211411e94fdf86519996b7f5f05e71ba34835d8f0c0f03c00a26271da02664"}, - {file = "black-25.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a3bb5ce32daa9ff0605d73b6f19da0b0e6c1f8f2d75594db539fdfed722f2b06"}, - {file = "black-25.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9815ccee1e55717fe9a4b924cae1646ef7f54e0f990da39a34fc7b264fcf80a2"}, - {file = "black-25.11.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:92285c37b93a1698dcbc34581867b480f1ba3a7b92acf1fe0467b04d7a4da0dc"}, - {file = "black-25.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:43945853a31099c7c0ff8dface53b4de56c41294fa6783c0441a8b1d9bf668bc"}, - {file = "black-25.11.0-py3-none-any.whl", hash = "sha256:e3f562da087791e96cefcd9dda058380a442ab322a02e222add53736451f604b"}, - {file = "black-25.11.0.tar.gz", hash = "sha256:9a323ac32f5dc75ce7470501b887250be5005a01602e931a15e45593f70f6e08"}, + {file = "black-26.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ca699710dece84e3ebf6e92ee15f5b8f72870ef984bf944a57a777a48357c168"}, + {file = "black-26.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5e8e75dabb6eb83d064b0db46392b25cabb6e784ea624219736e8985a6b3675d"}, + {file = "black-26.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb07665d9a907a1a645ee41a0df8a25ffac8ad9c26cdb557b7b88eeeeec934e0"}, + {file = "black-26.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:7ed300200918147c963c87700ccf9966dceaefbbb7277450a8d646fc5646bf24"}, + {file = "black-26.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:c5b7713daea9bf943f79f8c3b46f361cc5229e0e604dcef6a8bb6d1c37d9df89"}, + {file = "black-26.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3cee1487a9e4c640dc7467aaa543d6c0097c391dc8ac74eb313f2fbf9d7a7cb5"}, + {file = "black-26.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d62d14ca31c92adf561ebb2e5f2741bf8dea28aef6deb400d49cca011d186c68"}, + {file = "black-26.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb1dafbbaa3b1ee8b4550a84425aac8874e5f390200f5502cf3aee4a2acb2f14"}, + {file = "black-26.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:101540cb2a77c680f4f80e628ae98bd2bd8812fb9d72ade4f8995c5ff019e82c"}, + {file = "black-26.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:6f3977a16e347f1b115662be07daa93137259c711e526402aa444d7a88fdc9d4"}, + {file = "black-26.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6eeca41e70b5f5c84f2f913af857cf2ce17410847e1d54642e658e078da6544f"}, + {file = "black-26.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dd39eef053e58e60204f2cdf059e2442e2eb08f15989eefe259870f89614c8b6"}, + {file = "black-26.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9459ad0d6cd483eacad4c6566b0f8e42af5e8b583cee917d90ffaa3778420a0a"}, + {file = "black-26.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a19915ec61f3a8746e8b10adbac4a577c6ba9851fa4a9e9fbfbcf319887a5791"}, + {file = "black-26.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:643d27fb5facc167c0b1b59d0315f2674a6e950341aed0fc05cf307d22bf4954"}, + {file = "black-26.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ba1d768fbfb6930fc93b0ecc32a43d8861ded16f47a40f14afa9bb04ab93d304"}, + {file = "black-26.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2b807c240b64609cb0e80d2200a35b23c7df82259f80bef1b2c96eb422b4aac9"}, + {file = "black-26.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1de0f7d01cc894066a1153b738145b194414cc6eeaad8ef4397ac9abacf40f6b"}, + {file = "black-26.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:91a68ae46bf07868963671e4d05611b179c2313301bd756a89ad4e3b3db2325b"}, + {file = "black-26.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:be5e2fe860b9bd9edbf676d5b60a9282994c03fbbd40fe8f5e75d194f96064ca"}, + {file = "black-26.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9dc8c71656a79ca49b8d3e2ce8103210c9481c57798b48deeb3a8bb02db5f115"}, + {file = "black-26.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b22b3810451abe359a964cc88121d57f7bce482b53a066de0f1584988ca36e79"}, + {file = "black-26.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:53c62883b3f999f14e5d30b5a79bd437236658ad45b2f853906c7cbe79de00af"}, + {file = "black-26.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:f016baaadc423dc960cdddf9acae679e71ee02c4c341f78f3179d7e4819c095f"}, + {file = "black-26.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:66912475200b67ef5a0ab665011964bf924745103f51977a78b4fb92a9fc1bf0"}, + {file = "black-26.1.0-py3-none-any.whl", hash = "sha256:1054e8e47ebd686e078c0bb0eaf31e6ce69c966058d122f2c0c950311f9f3ede"}, + {file = "black-26.1.0.tar.gz", hash = "sha256:d294ac3340eef9c9eb5d29288e96dc719ff269a88e27b396340459dd85da4c58"}, ] [package.dependencies] click = ">=8.0.0" mypy-extensions = ">=0.4.3" packaging = ">=22.0" -pathspec = ">=0.9.0" +pathspec = ">=1.0.0" platformdirs = ">=2" pytokens = ">=0.3.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} @@ -1278,16 +1279,22 @@ files = [ [[package]] name = "pathspec" -version = "0.12.1" +version = "1.0.4" description = "Utility library for gitignore style pattern matching of file paths." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, + {file = "pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723"}, + {file = "pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645"}, ] +[package.extras] +hyperscan = ["hyperscan (>=0.7)"] +optional = ["typing-extensions (>=4)"] +re2 = ["google-re2 (>=1.1)"] +tests = ["pytest (>=9)", "typing-extensions (>=4.15)"] + [[package]] name = "platformdirs" version = "4.5.1" @@ -2076,4 +2083,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.10" -content-hash = "47b6dd96d689ecb027ffaf300f2c3410ee3f95c2c5c5ac9ac950d1e7d416bbb1" +content-hash = "c5b4a168edc0ff617f571f88cba606abb0623a6568240a1707d172db6f880da9" diff --git a/pyproject.toml b/pyproject.toml index b51e963..70e6491 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ requests = ">=2.5.0" polling2= "0.5.0" [tool.poetry.group.dev.dependencies] -black= "25.11.0" +black= "26.1.0" flake8= "7.3.0" pytest = ">=8.2" twine = ">=6.0" From f6cc213edb5373cebd176dc1d72e0aab3179bbd0 Mon Sep 17 00:00:00 2001 From: Benjamin Einaudi Date: Tue, 10 Feb 2026 18:05:24 +0100 Subject: [PATCH 252/264] prepare 1.40.3 --- cloudfoundry_client/__init__.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudfoundry_client/__init__.py b/cloudfoundry_client/__init__.py index 970ecdd..9100ff8 100644 --- a/cloudfoundry_client/__init__.py +++ b/cloudfoundry_client/__init__.py @@ -2,4 +2,4 @@ This module provides a client library for cloudfoundry_client v2/v3. """ -__version__ = "1.40.2" +__version__ = "1.40.3" diff --git a/pyproject.toml b/pyproject.toml index 70e6491..83b7d75 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ exclude = ''' [tool.poetry] name = "cloudfoundry_client" -version = "1.40.2" +version = "1.40.3" description = "A client library for CloudFoundry" authors = ["Benjamin Einaudi "] readme = "README.rst" From 1d22292cc0e272c052a1d8fabda8cc847ed86037 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 10:56:25 +0200 Subject: [PATCH 253/264] chore(deps): bump protobuf from 6.33.5 to 7.34.0 (#297) Bumps [protobuf](https://github.com/protocolbuffers/protobuf) from 6.33.5 to 7.34.0. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Commits](https://github.com/protocolbuffers/protobuf/commits) --- updated-dependencies: - dependency-name: protobuf dependency-version: 7.34.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 24 +++++++++++------------- pyproject.toml | 2 +- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/poetry.lock b/poetry.lock index a51fe5c..7fb3f07 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1474,22 +1474,20 @@ files = [ [[package]] name = "protobuf" -version = "6.33.5" +version = "7.34.0" description = "" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main"] files = [ - {file = "protobuf-6.33.5-cp310-abi3-win32.whl", hash = "sha256:d71b040839446bac0f4d162e758bea99c8251161dae9d0983a3b88dee345153b"}, - {file = "protobuf-6.33.5-cp310-abi3-win_amd64.whl", hash = "sha256:3093804752167bcab3998bec9f1048baae6e29505adaf1afd14a37bddede533c"}, - {file = "protobuf-6.33.5-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:a5cb85982d95d906df1e2210e58f8e4f1e3cdc088e52c921a041f9c9a0386de5"}, - {file = "protobuf-6.33.5-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:9b71e0281f36f179d00cbcb119cb19dec4d14a81393e5ea220f64b286173e190"}, - {file = "protobuf-6.33.5-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8afa18e1d6d20af15b417e728e9f60f3aa108ee76f23c3b2c07a2c3b546d3afd"}, - {file = "protobuf-6.33.5-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:cbf16ba3350fb7b889fca858fb215967792dc125b35c7976ca4818bee3521cf0"}, - {file = "protobuf-6.33.5-cp39-cp39-win32.whl", hash = "sha256:a3157e62729aafb8df6da2c03aa5c0937c7266c626ce11a278b6eb7963c4e37c"}, - {file = "protobuf-6.33.5-cp39-cp39-win_amd64.whl", hash = "sha256:8f04fa32763dcdb4973d537d6b54e615cc61108c7cb38fe59310c3192d29510a"}, - {file = "protobuf-6.33.5-py3-none-any.whl", hash = "sha256:69915a973dd0f60f31a08b8318b73eab2bd6a392c79184b3612226b0a3f8ec02"}, - {file = "protobuf-6.33.5.tar.gz", hash = "sha256:6ddcac2a081f8b7b9642c09406bc6a4290128fce5f471cddd165960bb9119e5c"}, + {file = "protobuf-7.34.0-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:8e329966799f2c271d5e05e236459fe1cbfdb8755aaa3b0914fa60947ddea408"}, + {file = "protobuf-7.34.0-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:9d7a5005fb96f3c1e64f397f91500b0eb371b28da81296ae73a6b08a5b76cdd6"}, + {file = "protobuf-7.34.0-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:4a72a8ec94e7a9f7ef7fe818ed26d073305f347f8b3b5ba31e22f81fd85fca02"}, + {file = "protobuf-7.34.0-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:964cf977e07f479c0697964e83deda72bcbc75c3badab506fb061b352d991b01"}, + {file = "protobuf-7.34.0-cp310-abi3-win32.whl", hash = "sha256:f791ec509707a1d91bd02e07df157e75e4fb9fbdad12a81b7396201ec244e2e3"}, + {file = "protobuf-7.34.0-cp310-abi3-win_amd64.whl", hash = "sha256:9f9079f1dde4e32342ecbd1c118d76367090d4aaa19da78230c38101c5b3dd40"}, + {file = "protobuf-7.34.0-py3-none-any.whl", hash = "sha256:e3b914dd77fa33fa06ab2baa97937746ab25695f389869afdf03e81f34e45dc7"}, + {file = "protobuf-7.34.0.tar.gz", hash = "sha256:3871a3df67c710aaf7bb8d214cc997342e63ceebd940c8c7fc65c9b3d697591a"}, ] [[package]] @@ -2083,4 +2081,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.10" -content-hash = "c5b4a168edc0ff617f571f88cba606abb0623a6568240a1707d172db6f880da9" +content-hash = "8193f8c0333cd3437ef60fa811fec285a075f3df40d360039c0de0298a0aebf7" diff --git a/pyproject.toml b/pyproject.toml index 83b7d75..a9eb38e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ keywords = ["cloudfoundry", "cf"] [tool.poetry.dependencies] python = ">=3.10" aiohttp = ">=3.8.0" -protobuf = "6.33.5" +protobuf = "7.34.0" oauth2-client= "1.4.2" websocket-client= "~1.9.0" PyYAML = ">=6.0" From 165fb7b71041bf9a537721351a530f836724df61 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 11:04:39 +0200 Subject: [PATCH 254/264] chore(deps-dev): bump black from 26.1.0 to 26.3.1 (#298) Bumps [black](https://github.com/psf/black) from 26.1.0 to 26.3.1. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/26.1.0...26.3.1) --- updated-dependencies: - dependency-name: black dependency-version: 26.3.1 dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 108 +++++++++++++++++++++++++++++++++---------------- pyproject.toml | 2 +- 2 files changed, 75 insertions(+), 35 deletions(-) diff --git a/poetry.lock b/poetry.lock index 7fb3f07..310fffb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -215,39 +215,39 @@ testing = ["jaraco.test", "pytest (!=8.0.*)", "pytest (>=6,!=8.1.*)", "pytest-ch [[package]] name = "black" -version = "26.1.0" +version = "26.3.1" description = "The uncompromising code formatter." optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "black-26.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ca699710dece84e3ebf6e92ee15f5b8f72870ef984bf944a57a777a48357c168"}, - {file = "black-26.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5e8e75dabb6eb83d064b0db46392b25cabb6e784ea624219736e8985a6b3675d"}, - {file = "black-26.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb07665d9a907a1a645ee41a0df8a25ffac8ad9c26cdb557b7b88eeeeec934e0"}, - {file = "black-26.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:7ed300200918147c963c87700ccf9966dceaefbbb7277450a8d646fc5646bf24"}, - {file = "black-26.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:c5b7713daea9bf943f79f8c3b46f361cc5229e0e604dcef6a8bb6d1c37d9df89"}, - {file = "black-26.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3cee1487a9e4c640dc7467aaa543d6c0097c391dc8ac74eb313f2fbf9d7a7cb5"}, - {file = "black-26.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d62d14ca31c92adf561ebb2e5f2741bf8dea28aef6deb400d49cca011d186c68"}, - {file = "black-26.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb1dafbbaa3b1ee8b4550a84425aac8874e5f390200f5502cf3aee4a2acb2f14"}, - {file = "black-26.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:101540cb2a77c680f4f80e628ae98bd2bd8812fb9d72ade4f8995c5ff019e82c"}, - {file = "black-26.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:6f3977a16e347f1b115662be07daa93137259c711e526402aa444d7a88fdc9d4"}, - {file = "black-26.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6eeca41e70b5f5c84f2f913af857cf2ce17410847e1d54642e658e078da6544f"}, - {file = "black-26.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dd39eef053e58e60204f2cdf059e2442e2eb08f15989eefe259870f89614c8b6"}, - {file = "black-26.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9459ad0d6cd483eacad4c6566b0f8e42af5e8b583cee917d90ffaa3778420a0a"}, - {file = "black-26.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a19915ec61f3a8746e8b10adbac4a577c6ba9851fa4a9e9fbfbcf319887a5791"}, - {file = "black-26.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:643d27fb5facc167c0b1b59d0315f2674a6e950341aed0fc05cf307d22bf4954"}, - {file = "black-26.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ba1d768fbfb6930fc93b0ecc32a43d8861ded16f47a40f14afa9bb04ab93d304"}, - {file = "black-26.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2b807c240b64609cb0e80d2200a35b23c7df82259f80bef1b2c96eb422b4aac9"}, - {file = "black-26.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1de0f7d01cc894066a1153b738145b194414cc6eeaad8ef4397ac9abacf40f6b"}, - {file = "black-26.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:91a68ae46bf07868963671e4d05611b179c2313301bd756a89ad4e3b3db2325b"}, - {file = "black-26.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:be5e2fe860b9bd9edbf676d5b60a9282994c03fbbd40fe8f5e75d194f96064ca"}, - {file = "black-26.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9dc8c71656a79ca49b8d3e2ce8103210c9481c57798b48deeb3a8bb02db5f115"}, - {file = "black-26.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b22b3810451abe359a964cc88121d57f7bce482b53a066de0f1584988ca36e79"}, - {file = "black-26.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:53c62883b3f999f14e5d30b5a79bd437236658ad45b2f853906c7cbe79de00af"}, - {file = "black-26.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:f016baaadc423dc960cdddf9acae679e71ee02c4c341f78f3179d7e4819c095f"}, - {file = "black-26.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:66912475200b67ef5a0ab665011964bf924745103f51977a78b4fb92a9fc1bf0"}, - {file = "black-26.1.0-py3-none-any.whl", hash = "sha256:1054e8e47ebd686e078c0bb0eaf31e6ce69c966058d122f2c0c950311f9f3ede"}, - {file = "black-26.1.0.tar.gz", hash = "sha256:d294ac3340eef9c9eb5d29288e96dc719ff269a88e27b396340459dd85da4c58"}, + {file = "black-26.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:86a8b5035fce64f5dcd1b794cf8ec4d31fe458cf6ce3986a30deb434df82a1d2"}, + {file = "black-26.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5602bdb96d52d2d0672f24f6ffe5218795736dd34807fd0fd55ccd6bf206168b"}, + {file = "black-26.3.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c54a4a82e291a1fee5137371ab488866b7c86a3305af4026bdd4dc78642e1ac"}, + {file = "black-26.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:6e131579c243c98f35bce64a7e08e87fb2d610544754675d4a0e73a070a5aa3a"}, + {file = "black-26.3.1-cp310-cp310-win_arm64.whl", hash = "sha256:5ed0ca58586c8d9a487352a96b15272b7fa55d139fc8496b519e78023a8dab0a"}, + {file = "black-26.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:28ef38aee69e4b12fda8dba75e21f9b4f979b490c8ac0baa7cb505369ac9e1ff"}, + {file = "black-26.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf9bf162ed91a26f1adba8efda0b573bc6924ec1408a52cc6f82cb73ec2b142c"}, + {file = "black-26.3.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:474c27574d6d7037c1bc875a81d9be0a9a4f9ee95e62800dab3cfaadbf75acd5"}, + {file = "black-26.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:5e9d0d86df21f2e1677cc4bd090cd0e446278bcbbe49bf3659c308c3e402843e"}, + {file = "black-26.3.1-cp311-cp311-win_arm64.whl", hash = "sha256:9a5e9f45e5d5e1c5b5c29b3bd4265dcc90e8b92cf4534520896ed77f791f4da5"}, + {file = "black-26.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e6f89631eb88a7302d416594a32faeee9fb8fb848290da9d0a5f2903519fc1"}, + {file = "black-26.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cd2012d35b47d589cb8a16faf8a32ef7a336f56356babd9fcf70939ad1897f"}, + {file = "black-26.3.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f76ff19ec5297dd8e66eb64deda23631e642c9393ab592826fd4bdc97a4bce7"}, + {file = "black-26.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:ddb113db38838eb9f043623ba274cfaf7d51d5b0c22ecb30afe58b1bb8322983"}, + {file = "black-26.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:dfdd51fc3e64ea4f35873d1b3fb25326773d55d2329ff8449139ebaad7357efb"}, + {file = "black-26.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:855822d90f884905362f602880ed8b5df1b7e3ee7d0db2502d4388a954cc8c54"}, + {file = "black-26.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8a33d657f3276328ce00e4d37fe70361e1ec7614da5d7b6e78de5426cb56332f"}, + {file = "black-26.3.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f1cd08e99d2f9317292a311dfe578fd2a24b15dbce97792f9c4d752275c1fa56"}, + {file = "black-26.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:c7e72339f841b5a237ff14f7d3880ddd0fc7f98a1199e8c4327f9a4f478c1839"}, + {file = "black-26.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:afc622538b430aa4c8c853f7f63bc582b3b8030fd8c80b70fb5fa5b834e575c2"}, + {file = "black-26.3.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2d6bfaf7fd0993b420bed691f20f9492d53ce9a2bcccea4b797d34e947318a78"}, + {file = "black-26.3.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f89f2ab047c76a9c03f78d0d66ca519e389519902fa27e7a91117ef7611c0568"}, + {file = "black-26.3.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b07fc0dab849d24a80a29cfab8d8a19187d1c4685d8a5e6385a5ce323c1f015f"}, + {file = "black-26.3.1-cp314-cp314-win_amd64.whl", hash = "sha256:0126ae5b7c09957da2bdbd91a9ba1207453feada9e9fe51992848658c6c8e01c"}, + {file = "black-26.3.1-cp314-cp314-win_arm64.whl", hash = "sha256:92c0ec1f2cc149551a2b7b47efc32c866406b6891b0ee4625e95967c8f4acfb1"}, + {file = "black-26.3.1-py3-none-any.whl", hash = "sha256:2bd5aa94fc267d38bb21a70d7410a89f1a1d318841855f698746f8e7f51acd1b"}, + {file = "black-26.3.1.tar.gz", hash = "sha256:2c50f5063a9641c7eed7795014ba37b0f5fa227f3d408b968936e24bc0566b07"}, ] [package.dependencies] @@ -256,7 +256,7 @@ mypy-extensions = ">=0.4.3" packaging = ">=22.0" pathspec = ">=1.0.0" platformdirs = ">=2" -pytokens = ">=0.3.0" +pytokens = ">=0.4.0,<0.5.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} @@ -264,7 +264,7 @@ typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} colorama = ["colorama (>=0.4.3)"] d = ["aiohttp (>=3.10)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] +uvloop = ["uvloop (>=0.15.2) ; sys_platform != \"win32\"", "winloop (>=0.5.0) ; sys_platform == \"win32\""] [[package]] name = "certifi" @@ -1568,14 +1568,54 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests [[package]] name = "pytokens" -version = "0.3.0" +version = "0.4.1" description = "A Fast, spec compliant Python 3.14+ tokenizer that runs on older Pythons." optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "pytokens-0.3.0-py3-none-any.whl", hash = "sha256:95b2b5eaf832e469d141a378872480ede3f251a5a5041b8ec6e581d3ac71bbf3"}, - {file = "pytokens-0.3.0.tar.gz", hash = "sha256:2f932b14ed08de5fcf0b391ace2642f858f1394c0857202959000b68ed7a458a"}, + {file = "pytokens-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a44ed93ea23415c54f3face3b65ef2b844d96aeb3455b8a69b3df6beab6acc5"}, + {file = "pytokens-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:add8bf86b71a5d9fb5b89f023a80b791e04fba57960aa790cc6125f7f1d39dfe"}, + {file = "pytokens-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:670d286910b531c7b7e3c0b453fd8156f250adb140146d234a82219459b9640c"}, + {file = "pytokens-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4e691d7f5186bd2842c14813f79f8884bb03f5995f0575272009982c5ac6c0f7"}, + {file = "pytokens-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:27b83ad28825978742beef057bfe406ad6ed524b2d28c252c5de7b4a6dd48fa2"}, + {file = "pytokens-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d70e77c55ae8380c91c0c18dea05951482e263982911fc7410b1ffd1dadd3440"}, + {file = "pytokens-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a58d057208cb9075c144950d789511220b07636dd2e4708d5645d24de666bdc"}, + {file = "pytokens-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b49750419d300e2b5a3813cf229d4e5a4c728dae470bcc89867a9ad6f25a722d"}, + {file = "pytokens-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9907d61f15bf7261d7e775bd5d7ee4d2930e04424bab1972591918497623a16"}, + {file = "pytokens-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee44d0f85b803321710f9239f335aafe16553b39106384cef8e6de40cb4ef2f6"}, + {file = "pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083"}, + {file = "pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1"}, + {file = "pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1"}, + {file = "pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9"}, + {file = "pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68"}, + {file = "pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b"}, + {file = "pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f"}, + {file = "pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1"}, + {file = "pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4"}, + {file = "pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78"}, + {file = "pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321"}, + {file = "pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa"}, + {file = "pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d"}, + {file = "pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324"}, + {file = "pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9"}, + {file = "pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb"}, + {file = "pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3"}, + {file = "pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975"}, + {file = "pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a"}, + {file = "pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918"}, + {file = "pytokens-0.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:da5baeaf7116dced9c6bb76dc31ba04a2dc3695f3d9f74741d7910122b456edc"}, + {file = "pytokens-0.4.1-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11edda0942da80ff58c4408407616a310adecae1ddd22eef8c692fe266fa5009"}, + {file = "pytokens-0.4.1-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0fc71786e629cef478cbf29d7ea1923299181d0699dbe7c3c0f4a583811d9fc1"}, + {file = "pytokens-0.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dcafc12c30dbaf1e2af0490978352e0c4041a7cde31f4f81435c2a5e8b9cabb6"}, + {file = "pytokens-0.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:42f144f3aafa5d92bad964d471a581651e28b24434d184871bd02e3a0d956037"}, + {file = "pytokens-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:34bcc734bd2f2d5fe3b34e7b3c0116bfb2397f2d9666139988e7a3eb5f7400e3"}, + {file = "pytokens-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:941d4343bf27b605e9213b26bfa1c4bf197c9c599a9627eb7305b0defcfe40c1"}, + {file = "pytokens-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3ad72b851e781478366288743198101e5eb34a414f1d5627cdd585ca3b25f1db"}, + {file = "pytokens-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:682fa37ff4d8e95f7df6fe6fe6a431e8ed8e788023c6bcc0f0880a12eab80ad1"}, + {file = "pytokens-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:30f51edd9bb7f85c748979384165601d028b84f7bd13fe14d3e065304093916a"}, + {file = "pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de"}, + {file = "pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a"}, ] [package.extras] @@ -2081,4 +2121,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.10" -content-hash = "8193f8c0333cd3437ef60fa811fec285a075f3df40d360039c0de0298a0aebf7" +content-hash = "9b5731069058038790c505494d6557eda29250ddb95003e26c43bd4d15ad560e" diff --git a/pyproject.toml b/pyproject.toml index a9eb38e..2244394 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ requests = ">=2.5.0" polling2= "0.5.0" [tool.poetry.group.dev.dependencies] -black= "26.1.0" +black= "26.3.1" flake8= "7.3.0" pytest = ">=8.2" twine = ">=6.0" From d6c232ad9820309a3610157cc2d295d3b4dcc7b7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 11:05:14 +0200 Subject: [PATCH 255/264] chore(deps): bump requests from 2.32.5 to 2.33.0 (#299) Bumps [requests](https://github.com/psf/requests) from 2.32.5 to 2.33.0. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.32.5...v2.33.0) --- updated-dependencies: - dependency-name: requests dependency-version: 2.33.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/poetry.lock b/poetry.lock index 310fffb..3bb8247 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1739,25 +1739,26 @@ md = ["cmarkgfm (>=0.8.0)"] [[package]] name = "requests" -version = "2.32.5" +version = "2.33.0" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main", "dev"] files = [ - {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"}, - {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"}, + {file = "requests-2.33.0-py3-none-any.whl", hash = "sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b"}, + {file = "requests-2.33.0.tar.gz", hash = "sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652"}, ] [package.dependencies] -certifi = ">=2017.4.17" +certifi = ">=2023.5.7" charset_normalizer = ">=2,<4" idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" +urllib3 = ">=1.26,<3" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +test = ["PySocks (>=1.5.6,!=1.5.7)", "pytest (>=3)", "pytest-cov", "pytest-httpbin (==2.1.0)", "pytest-mock", "pytest-xdist"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<8)"] [[package]] name = "requests-toolbelt" From 1116356f2b8b2f7c1588f86c39533b9d9cd1a520 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 11:05:43 +0200 Subject: [PATCH 256/264] chore(deps-dev): bump cryptography from 46.0.3 to 46.0.6 (#300) Bumps [cryptography](https://github.com/pyca/cryptography) from 46.0.3 to 46.0.6. - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pyca/cryptography/compare/46.0.3...46.0.6) --- updated-dependencies: - dependency-name: cryptography dependency-version: 46.0.6 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 112 +++++++++++++++++++++++++--------------------------- 1 file changed, 53 insertions(+), 59 deletions(-) diff --git a/poetry.lock b/poetry.lock index 3bb8247..a2d7823 100644 --- a/poetry.lock +++ b/poetry.lock @@ -285,7 +285,7 @@ description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.9" groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\"" +markers = "platform_python_implementation != \"PyPy\"" files = [ {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, {file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"}, @@ -529,67 +529,61 @@ files = [ [[package]] name = "cryptography" -version = "46.0.3" +version = "46.0.6" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.8" groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" files = [ - {file = "cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a"}, - {file = "cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc"}, - {file = "cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d"}, - {file = "cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb"}, - {file = "cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849"}, - {file = "cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8"}, - {file = "cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec"}, - {file = "cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91"}, - {file = "cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e"}, - {file = "cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926"}, - {file = "cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71"}, - {file = "cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac"}, - {file = "cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018"}, - {file = "cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb"}, - {file = "cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c"}, - {file = "cryptography-46.0.3-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:00a5e7e87938e5ff9ff5447ab086a5706a957137e6e433841e9d24f38a065217"}, - {file = "cryptography-46.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8daeb2d2174beb4575b77482320303f3d39b8e81153da4f0fb08eb5fe86a6c5"}, - {file = "cryptography-46.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39b6755623145ad5eff1dab323f4eae2a32a77a7abef2c5089a04a3d04366715"}, - {file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:db391fa7c66df6762ee3f00c95a89e6d428f4d60e7abc8328f4fe155b5ac6e54"}, - {file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:78a97cf6a8839a48c49271cdcbd5cf37ca2c1d6b7fdd86cc864f302b5e9bf459"}, - {file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:dfb781ff7eaa91a6f7fd41776ec37c5853c795d3b358d4896fdbb5df168af422"}, - {file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6f61efb26e76c45c4a227835ddeae96d83624fb0d29eb5df5b96e14ed1a0afb7"}, - {file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:23b1a8f26e43f47ceb6d6a43115f33a5a37d57df4ea0ca295b780ae8546e8044"}, - {file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b419ae593c86b87014b9be7396b385491ad7f320bde96826d0dd174459e54665"}, - {file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:50fc3343ac490c6b08c0cf0d704e881d0d660be923fd3076db3e932007e726e3"}, - {file = "cryptography-46.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22d7e97932f511d6b0b04f2bfd818d73dcd5928db509460aaf48384778eb6d20"}, - {file = "cryptography-46.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d55f3dffadd674514ad19451161118fd010988540cee43d8bc20675e775925de"}, - {file = "cryptography-46.0.3-cp314-cp314t-win32.whl", hash = "sha256:8a6e050cb6164d3f830453754094c086ff2d0b2f3a897a1d9820f6139a1f0914"}, - {file = "cryptography-46.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:760f83faa07f8b64e9c33fc963d790a2edb24efb479e3520c14a45741cd9b2db"}, - {file = "cryptography-46.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:516ea134e703e9fe26bcd1277a4b59ad30586ea90c365a87781d7887a646fe21"}, - {file = "cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936"}, - {file = "cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683"}, - {file = "cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d"}, - {file = "cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0"}, - {file = "cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc"}, - {file = "cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3"}, - {file = "cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971"}, - {file = "cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac"}, - {file = "cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04"}, - {file = "cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506"}, - {file = "cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963"}, - {file = "cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4"}, - {file = "cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df"}, - {file = "cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f"}, - {file = "cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372"}, - {file = "cryptography-46.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a23582810fedb8c0bc47524558fb6c56aac3fc252cb306072fd2815da2a47c32"}, - {file = "cryptography-46.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e7aec276d68421f9574040c26e2a7c3771060bc0cff408bae1dcb19d3ab1e63c"}, - {file = "cryptography-46.0.3-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7ce938a99998ed3c8aa7e7272dca1a610401ede816d36d0693907d863b10d9ea"}, - {file = "cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:191bb60a7be5e6f54e30ba16fdfae78ad3a342a0599eb4193ba88e3f3d6e185b"}, - {file = "cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c70cc23f12726be8f8bc72e41d5065d77e4515efae3690326764ea1b07845cfb"}, - {file = "cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:9394673a9f4de09e28b5356e7fff97d778f8abad85c9d5ac4a4b7e25a0de7717"}, - {file = "cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:94cd0549accc38d1494e1f8de71eca837d0509d0d44bf11d158524b0e12cebf9"}, - {file = "cryptography-46.0.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6b5063083824e5509fdba180721d55909ffacccc8adbec85268b48439423d78c"}, - {file = "cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1"}, + {file = "cryptography-46.0.6-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19"}, + {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738"}, + {file = "cryptography-46.0.6-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c"}, + {file = "cryptography-46.0.6-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f"}, + {file = "cryptography-46.0.6-cp311-abi3-win32.whl", hash = "sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2"}, + {file = "cryptography-46.0.6-cp311-abi3-win_amd64.whl", hash = "sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124"}, + {file = "cryptography-46.0.6-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4"}, + {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a"}, + {file = "cryptography-46.0.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d"}, + {file = "cryptography-46.0.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736"}, + {file = "cryptography-46.0.6-cp314-cp314t-win32.whl", hash = "sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed"}, + {file = "cryptography-46.0.6-cp314-cp314t-win_amd64.whl", hash = "sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4"}, + {file = "cryptography-46.0.6-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa"}, + {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58"}, + {file = "cryptography-46.0.6-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb"}, + {file = "cryptography-46.0.6-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72"}, + {file = "cryptography-46.0.6-cp38-abi3-win32.whl", hash = "sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c"}, + {file = "cryptography-46.0.6-cp38-abi3-win_amd64.whl", hash = "sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f"}, + {file = "cryptography-46.0.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead"}, + {file = "cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8"}, + {file = "cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0"}, + {file = "cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b"}, + {file = "cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a"}, + {file = "cryptography-46.0.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e"}, + {file = "cryptography-46.0.6.tar.gz", hash = "sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759"}, ] [package.dependencies] @@ -603,7 +597,7 @@ nox = ["nox[uv] (>=2024.4.15)"] pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.14)", "ruff (>=0.11.11)"] sdist = ["build (>=1.0.0)"] ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi (>=2024)", "cryptography-vectors (==46.0.3)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] +test = ["certifi (>=2024)", "cryptography-vectors (==46.0.6)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] test-randomorder = ["pytest-randomly"] [[package]] @@ -1509,7 +1503,7 @@ description = "C parser in Python" optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\"" +markers = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\"" files = [ {file = "pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"}, {file = "pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"}, From b27bd8e527ba50069ae52bc5f16d7a16cb9e1708 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 11:06:01 +0200 Subject: [PATCH 257/264] chore(deps-dev): bump pygments from 2.19.2 to 2.20.0 (#301) Bumps [pygments](https://github.com/pygments/pygments) from 2.19.2 to 2.20.0. - [Release notes](https://github.com/pygments/pygments/releases) - [Changelog](https://github.com/pygments/pygments/blob/master/CHANGES) - [Commits](https://github.com/pygments/pygments/compare/2.19.2...2.20.0) --- updated-dependencies: - dependency-name: pygments dependency-version: 2.20.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index a2d7823..fb1c221 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1523,14 +1523,14 @@ files = [ [[package]] name = "pygments" -version = "2.19.2" +version = "2.20.0" description = "Pygments is a syntax highlighting package written in Python." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, - {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, + {file = "pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176"}, + {file = "pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f"}, ] [package.extras] From 216a633b693f8bd977de35eb2aadb13703487afb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 10:58:53 +0200 Subject: [PATCH 258/264] chore(deps): bump idna from 3.11 to 3.15 (#309) Bumps [idna](https://github.com/kjd/idna) from 3.11 to 3.15. - [Release notes](https://github.com/kjd/idna/releases) - [Changelog](https://github.com/kjd/idna/blob/master/HISTORY.md) - [Commits](https://github.com/kjd/idna/compare/v3.11...v3.15) --- updated-dependencies: - dependency-name: idna dependency-version: '3.15' dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/poetry.lock b/poetry.lock index fb1c221..2e800d3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -285,7 +285,7 @@ description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.9" groups = ["dev"] -markers = "platform_python_implementation != \"PyPy\"" +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\"" files = [ {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, {file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"}, @@ -534,6 +534,7 @@ description = "cryptography is a package which provides cryptographic recipes an optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.8" groups = ["dev"] +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" files = [ {file = "cryptography-46.0.6-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8"}, {file = "cryptography-46.0.6-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30"}, @@ -810,18 +811,18 @@ test = ["coverage[toml]", "pretend", "pytest", "pytest-cov"] [[package]] name = "idna" -version = "3.11" +version = "3.15" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.8" groups = ["main", "dev"] files = [ - {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, - {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, + {file = "idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8"}, + {file = "idna-3.15.tar.gz", hash = "sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc"}, ] [package.extras] -all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] +all = ["mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] [[package]] name = "importlib-metadata" @@ -1503,7 +1504,7 @@ description = "C parser in Python" optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\"" +markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\"" files = [ {file = "pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"}, {file = "pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"}, From 9fb0a7e42dee7e2bab51e4942b7136cb5c73d73b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 10:59:16 +0200 Subject: [PATCH 259/264] chore(deps): bump urllib3 from 2.6.3 to 2.7.0 (#308) Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.6.3 to 2.7.0. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/2.6.3...2.7.0) --- updated-dependencies: - dependency-name: urllib3 dependency-version: 2.7.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 2e800d3..17a46d4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1915,14 +1915,14 @@ markers = {main = "python_version < \"3.13\"", dev = "python_version == \"3.10\" [[package]] name = "urllib3" -version = "2.6.3" +version = "2.7.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main", "dev"] files = [ - {file = "urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4"}, - {file = "urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed"}, + {file = "urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897"}, + {file = "urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c"}, ] [package.extras] From 69ee54f83b29ac6a45869aa144b3fde9856e629b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 10:59:33 +0200 Subject: [PATCH 260/264] chore(deps-dev): bump pytest from 9.0.2 to 9.0.3 (#307) Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.2 to 9.0.3. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/9.0.2...9.0.3) --- updated-dependencies: - dependency-name: pytest dependency-version: 9.0.3 dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 17a46d4..8b43074 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1539,14 +1539,14 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pytest" -version = "9.0.2" +version = "9.0.3" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b"}, - {file = "pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11"}, + {file = "pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9"}, + {file = "pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c"}, ] [package.dependencies] From b297aaf4ddfa48214c59637cf4fe1a315d758ebc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 11:01:04 +0200 Subject: [PATCH 261/264] chore(deps-dev): bump cryptography from 46.0.6 to 46.0.7 (#306) Bumps [cryptography](https://github.com/pyca/cryptography) from 46.0.6 to 46.0.7. - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pyca/cryptography/compare/46.0.6...46.0.7) --- updated-dependencies: - dependency-name: cryptography dependency-version: 46.0.7 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 102 ++++++++++++++++++++++++++-------------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/poetry.lock b/poetry.lock index 8b43074..fcb068e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -529,62 +529,62 @@ files = [ [[package]] name = "cryptography" -version = "46.0.6" +version = "46.0.7" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.8" groups = ["dev"] markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" files = [ - {file = "cryptography-46.0.6-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738"}, - {file = "cryptography-46.0.6-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c"}, - {file = "cryptography-46.0.6-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f"}, - {file = "cryptography-46.0.6-cp311-abi3-win32.whl", hash = "sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2"}, - {file = "cryptography-46.0.6-cp311-abi3-win_amd64.whl", hash = "sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124"}, - {file = "cryptography-46.0.6-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a"}, - {file = "cryptography-46.0.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d"}, - {file = "cryptography-46.0.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736"}, - {file = "cryptography-46.0.6-cp314-cp314t-win32.whl", hash = "sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed"}, - {file = "cryptography-46.0.6-cp314-cp314t-win_amd64.whl", hash = "sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4"}, - {file = "cryptography-46.0.6-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58"}, - {file = "cryptography-46.0.6-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb"}, - {file = "cryptography-46.0.6-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72"}, - {file = "cryptography-46.0.6-cp38-abi3-win32.whl", hash = "sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c"}, - {file = "cryptography-46.0.6-cp38-abi3-win_amd64.whl", hash = "sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f"}, - {file = "cryptography-46.0.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead"}, - {file = "cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8"}, - {file = "cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0"}, - {file = "cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b"}, - {file = "cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a"}, - {file = "cryptography-46.0.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e"}, - {file = "cryptography-46.0.6.tar.gz", hash = "sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759"}, + {file = "cryptography-46.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:ea42cbe97209df307fdc3b155f1b6fa2577c0defa8f1f7d3be7d31d189108ad4"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5ad9ef796328c5e3c4ceed237a183f5d41d21150f972455a9d926593a1dcb308"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:73510b83623e080a2c35c62c15298096e2a5dc8d51c3b4e1740211839d0dea77"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cbd5fb06b62bd0721e1170273d3f4d5a277044c47ca27ee257025146c34cbdd1"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:420b1e4109cc95f0e5700eed79908cef9268265c773d3a66f7af1eef53d409ef"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:24402210aa54baae71d99441d15bb5a1919c195398a87b563df84468160a65de"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8a469028a86f12eb7d2fe97162d0634026d92a21f3ae0ac87ed1c4a447886c83"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9694078c5d44c157ef3162e3bf3946510b857df5a3955458381d1c7cfc143ddb"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:42a1e5f98abb6391717978baf9f90dc28a743b7d9be7f0751a6f56a75d14065b"}, + {file = "cryptography-46.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91bbcb08347344f810cbe49065914fe048949648f6bd5c2519f34619142bbe85"}, + {file = "cryptography-46.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5d1c02a14ceb9148cc7816249f64f623fbfee39e8c03b3650d842ad3f34d637e"}, + {file = "cryptography-46.0.7-cp311-abi3-win32.whl", hash = "sha256:d23c8ca48e44ee015cd0a54aeccdf9f09004eba9fc96f38c911011d9ff1bd457"}, + {file = "cryptography-46.0.7-cp311-abi3-win_amd64.whl", hash = "sha256:397655da831414d165029da9bc483bed2fe0e75dde6a1523ec2fe63f3c46046b"}, + {file = "cryptography-46.0.7-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:d151173275e1728cf7839aaa80c34fe550c04ddb27b34f48c232193df8db5842"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:db0f493b9181c7820c8134437eb8b0b4792085d37dbb24da050476ccb664e59c"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ebd6daf519b9f189f85c479427bbd6e9c9037862cf8fe89ee35503bd209ed902"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:b7b412817be92117ec5ed95f880defe9cf18a832e8cafacf0a22337dc1981b4d"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:fbfd0e5f273877695cb93baf14b185f4878128b250cc9f8e617ea0c025dfb022"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:ffca7aa1d00cf7d6469b988c581598f2259e46215e0140af408966a24cf086ce"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:60627cf07e0d9274338521205899337c5d18249db56865f943cbe753aa96f40f"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:80406c3065e2c55d7f49a9550fe0c49b3f12e5bfff5dedb727e319e1afb9bf99"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:c5b1ccd1239f48b7151a65bc6dd54bcfcc15e028c8ac126d3fada09db0e07ef1"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:d5f7520159cd9c2154eb61eb67548ca05c5774d39e9c2c4339fd793fe7d097b2"}, + {file = "cryptography-46.0.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fcd8eac50d9138c1d7fc53a653ba60a2bee81a505f9f8850b6b2888555a45d0e"}, + {file = "cryptography-46.0.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:65814c60f8cc400c63131584e3e1fad01235edba2614b61fbfbfa954082db0ee"}, + {file = "cryptography-46.0.7-cp314-cp314t-win32.whl", hash = "sha256:fdd1736fed309b4300346f88f74cd120c27c56852c3838cab416e7a166f67298"}, + {file = "cryptography-46.0.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e06acf3c99be55aa3b516397fe42f5855597f430add9c17fa46bf2e0fb34c9bb"}, + {file = "cryptography-46.0.7-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:462ad5cb1c148a22b2e3bcc5ad52504dff325d17daf5df8d88c17dda1f75f2a4"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:84d4cced91f0f159a7ddacad249cc077e63195c36aac40b4150e7a57e84fffe7"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:128c5edfe5e5938b86b03941e94fac9ee793a94452ad1365c9fc3f4f62216832"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5e51be372b26ef4ba3de3c167cd3d1022934bc838ae9eaad7e644986d2a3d163"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cdf1a610ef82abb396451862739e3fc93b071c844399e15b90726ef7470eeaf2"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1d25aee46d0c6f1a501adcddb2d2fee4b979381346a78558ed13e50aa8a59067"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:cdfbe22376065ffcf8be74dc9a909f032df19bc58a699456a21712d6e5eabfd0"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:abad9dac36cbf55de6eb49badd4016806b3165d396f64925bf2999bcb67837ba"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:935ce7e3cfdb53e3536119a542b839bb94ec1ad081013e9ab9b7cfd478b05006"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:35719dc79d4730d30f1c2b6474bd6acda36ae2dfae1e3c16f2051f215df33ce0"}, + {file = "cryptography-46.0.7-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7bbc6ccf49d05ac8f7d7b5e2e2c33830d4fe2061def88210a126d130d7f71a85"}, + {file = "cryptography-46.0.7-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a1529d614f44b863a7b480c6d000fe93b59acee9c82ffa027cfadc77521a9f5e"}, + {file = "cryptography-46.0.7-cp38-abi3-win32.whl", hash = "sha256:f247c8c1a1fb45e12586afbb436ef21ff1e80670b2861a90353d9b025583d246"}, + {file = "cryptography-46.0.7-cp38-abi3-win_amd64.whl", hash = "sha256:506c4ff91eff4f82bdac7633318a526b1d1309fc07ca76a3ad182cb5b686d6d3"}, + {file = "cryptography-46.0.7-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:fc9ab8856ae6cf7c9358430e49b368f3108f050031442eaeb6b9d87e4dcf4e4f"}, + {file = "cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d3b99c535a9de0adced13d159c5a9cf65c325601aa30f4be08afd680643e9c15"}, + {file = "cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d02c738dacda7dc2a74d1b2b3177042009d5cab7c7079db74afc19e56ca1b455"}, + {file = "cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:04959522f938493042d595a736e7dbdff6eb6cc2339c11465b3ff89343b65f65"}, + {file = "cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:3986ac1dee6def53797289999eabe84798ad7817f3e97779b5061a95b0ee4968"}, + {file = "cryptography-46.0.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:258514877e15963bd43b558917bc9f54cf7cf866c38aa576ebf47a77ddbc43a4"}, + {file = "cryptography-46.0.7.tar.gz", hash = "sha256:e4cfd68c5f3e0bfdad0d38e023239b96a2fe84146481852dffbcca442c245aa5"}, ] [package.dependencies] @@ -598,7 +598,7 @@ nox = ["nox[uv] (>=2024.4.15)"] pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.14)", "ruff (>=0.11.11)"] sdist = ["build (>=1.0.0)"] ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi (>=2024)", "cryptography-vectors (==46.0.6)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] +test = ["certifi (>=2024)", "cryptography-vectors (==46.0.7)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] test-randomorder = ["pytest-randomly"] [[package]] From 78e03f7d7329a3ff2b0558a8114a6754b31dea9c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 11:01:51 +0200 Subject: [PATCH 262/264] chore(deps): bump protobuf from 7.34.0 to 7.34.1 (#305) Bumps [protobuf](https://github.com/protocolbuffers/protobuf) from 7.34.0 to 7.34.1. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Commits](https://github.com/protocolbuffers/protobuf/commits) --- updated-dependencies: - dependency-name: protobuf dependency-version: 7.34.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 20 ++++++++++---------- pyproject.toml | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/poetry.lock b/poetry.lock index fcb068e..14dde9a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1469,20 +1469,20 @@ files = [ [[package]] name = "protobuf" -version = "7.34.0" +version = "7.34.1" description = "" optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "protobuf-7.34.0-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:8e329966799f2c271d5e05e236459fe1cbfdb8755aaa3b0914fa60947ddea408"}, - {file = "protobuf-7.34.0-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:9d7a5005fb96f3c1e64f397f91500b0eb371b28da81296ae73a6b08a5b76cdd6"}, - {file = "protobuf-7.34.0-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:4a72a8ec94e7a9f7ef7fe818ed26d073305f347f8b3b5ba31e22f81fd85fca02"}, - {file = "protobuf-7.34.0-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:964cf977e07f479c0697964e83deda72bcbc75c3badab506fb061b352d991b01"}, - {file = "protobuf-7.34.0-cp310-abi3-win32.whl", hash = "sha256:f791ec509707a1d91bd02e07df157e75e4fb9fbdad12a81b7396201ec244e2e3"}, - {file = "protobuf-7.34.0-cp310-abi3-win_amd64.whl", hash = "sha256:9f9079f1dde4e32342ecbd1c118d76367090d4aaa19da78230c38101c5b3dd40"}, - {file = "protobuf-7.34.0-py3-none-any.whl", hash = "sha256:e3b914dd77fa33fa06ab2baa97937746ab25695f389869afdf03e81f34e45dc7"}, - {file = "protobuf-7.34.0.tar.gz", hash = "sha256:3871a3df67c710aaf7bb8d214cc997342e63ceebd940c8c7fc65c9b3d697591a"}, + {file = "protobuf-7.34.1-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:d8b2cc79c4d8f62b293ad9b11ec3aebce9af481fa73e64556969f7345ebf9fc7"}, + {file = "protobuf-7.34.1-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:5185e0e948d07abe94bb76ec9b8416b604cfe5da6f871d67aad30cbf24c3110b"}, + {file = "protobuf-7.34.1-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:403b093a6e28a960372b44e5eb081775c9b056e816a8029c61231743d63f881a"}, + {file = "protobuf-7.34.1-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:8ff40ce8cd688f7265326b38d5a1bed9bfdf5e6723d49961432f83e21d5713e4"}, + {file = "protobuf-7.34.1-cp310-abi3-win32.whl", hash = "sha256:34b84ce27680df7cca9f231043ada0daa55d0c44a2ddfaa58ec1d0d89d8bf60a"}, + {file = "protobuf-7.34.1-cp310-abi3-win_amd64.whl", hash = "sha256:e97b55646e6ce5cbb0954a8c28cd39a5869b59090dfaa7df4598a7fba869468c"}, + {file = "protobuf-7.34.1-py3-none-any.whl", hash = "sha256:bb3812cd53aefea2b028ef42bd780f5b96407247f20c6ef7c679807e9d188f11"}, + {file = "protobuf-7.34.1.tar.gz", hash = "sha256:9ce42245e704cc5027be797c1db1eb93184d44d1cdd71811fb2d9b25ad541280"}, ] [[package]] @@ -2117,4 +2117,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.10" -content-hash = "9b5731069058038790c505494d6557eda29250ddb95003e26c43bd4d15ad560e" +content-hash = "0868efc896949f3bab524ef8479b28d82339ae66af946678a93116a94fe02f87" diff --git a/pyproject.toml b/pyproject.toml index 2244394..1d206ae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ keywords = ["cloudfoundry", "cf"] [tool.poetry.dependencies] python = ">=3.10" aiohttp = ">=3.8.0" -protobuf = "7.34.0" +protobuf = "7.34.1" oauth2-client= "1.4.2" websocket-client= "~1.9.0" PyYAML = ">=6.0" From 1340927d8eb6c1bbe7286146760f80c07aaa1e1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 11:02:26 +0200 Subject: [PATCH 263/264] chore(deps): bump aiohttp from 3.13.3 to 3.13.5 (#304) --- updated-dependencies: - dependency-name: aiohttp dependency-version: 3.13.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 242 ++++++++++++++++++++++++++-------------------------- 1 file changed, 121 insertions(+), 121 deletions(-) diff --git a/poetry.lock b/poetry.lock index 14dde9a..1520948 100644 --- a/poetry.lock +++ b/poetry.lock @@ -14,132 +14,132 @@ files = [ [[package]] name = "aiohttp" -version = "3.13.3" +version = "3.13.5" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "aiohttp-3.13.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a372fd5afd301b3a89582817fdcdb6c34124787c70dbcc616f259013e7eef7"}, - {file = "aiohttp-3.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:147e422fd1223005c22b4fe080f5d93ced44460f5f9c105406b753612b587821"}, - {file = "aiohttp-3.13.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:859bd3f2156e81dd01432f5849fc73e2243d4a487c4fd26609b1299534ee1845"}, - {file = "aiohttp-3.13.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dca68018bf48c251ba17c72ed479f4dafe9dbd5a73707ad8d28a38d11f3d42af"}, - {file = "aiohttp-3.13.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fee0c6bc7db1de362252affec009707a17478a00ec69f797d23ca256e36d5940"}, - {file = "aiohttp-3.13.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c048058117fd649334d81b4b526e94bde3ccaddb20463a815ced6ecbb7d11160"}, - {file = "aiohttp-3.13.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:215a685b6fbbfcf71dfe96e3eba7a6f58f10da1dfdf4889c7dd856abe430dca7"}, - {file = "aiohttp-3.13.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2c184bb1fe2cbd2cefba613e9db29a5ab559323f994b6737e370d3da0ac455"}, - {file = "aiohttp-3.13.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75ca857eba4e20ce9f546cd59c7007b33906a4cd48f2ff6ccf1ccfc3b646f279"}, - {file = "aiohttp-3.13.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81e97251d9298386c2b7dbeb490d3d1badbdc69107fb8c9299dd04eb39bddc0e"}, - {file = "aiohttp-3.13.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c0e2d366af265797506f0283487223146af57815b388623f0357ef7eac9b209d"}, - {file = "aiohttp-3.13.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4e239d501f73d6db1522599e14b9b321a7e3b1de66ce33d53a765d975e9f4808"}, - {file = "aiohttp-3.13.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0db318f7a6f065d84cb1e02662c526294450b314a02bd9e2a8e67f0d8564ce40"}, - {file = "aiohttp-3.13.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:bfc1cc2fe31a6026a8a88e4ecfb98d7f6b1fec150cfd708adbfd1d2f42257c29"}, - {file = "aiohttp-3.13.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af71fff7bac6bb7508956696dce8f6eec2bbb045eceb40343944b1ae62b5ef11"}, - {file = "aiohttp-3.13.3-cp310-cp310-win32.whl", hash = "sha256:37da61e244d1749798c151421602884db5270faf479cf0ef03af0ff68954c9dd"}, - {file = "aiohttp-3.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:7e63f210bc1b57ef699035f2b4b6d9ce096b5914414a49b0997c839b2bd2223c"}, - {file = "aiohttp-3.13.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b6073099fb654e0a068ae678b10feff95c5cae95bbfcbfa7af669d361a8aa6b"}, - {file = "aiohttp-3.13.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cb93e166e6c28716c8c6aeb5f99dfb6d5ccf482d29fe9bf9a794110e6d0ab64"}, - {file = "aiohttp-3.13.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:28e027cf2f6b641693a09f631759b4d9ce9165099d2b5d92af9bd4e197690eea"}, - {file = "aiohttp-3.13.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3b61b7169ababd7802f9568ed96142616a9118dd2be0d1866e920e77ec8fa92a"}, - {file = "aiohttp-3.13.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:80dd4c21b0f6237676449c6baaa1039abae86b91636b6c91a7f8e61c87f89540"}, - {file = "aiohttp-3.13.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:65d2ccb7eabee90ce0503c17716fc77226be026dcc3e65cce859a30db715025b"}, - {file = "aiohttp-3.13.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b179331a481cb5529fca8b432d8d3c7001cb217513c94cd72d668d1248688a3"}, - {file = "aiohttp-3.13.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d4c940f02f49483b18b079d1c27ab948721852b281f8b015c058100e9421dd1"}, - {file = "aiohttp-3.13.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f9444f105664c4ce47a2a7171a2418bce5b7bae45fb610f4e2c36045d85911d3"}, - {file = "aiohttp-3.13.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:694976222c711d1d00ba131904beb60534f93966562f64440d0c9d41b8cdb440"}, - {file = "aiohttp-3.13.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f33ed1a2bf1997a36661874b017f5c4b760f41266341af36febaf271d179f6d7"}, - {file = "aiohttp-3.13.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e636b3c5f61da31a92bf0d91da83e58fdfa96f178ba682f11d24f31944cdd28c"}, - {file = "aiohttp-3.13.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5d2d94f1f5fcbe40838ac51a6ab5704a6f9ea42e72ceda48de5e6b898521da51"}, - {file = "aiohttp-3.13.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2be0e9ccf23e8a94f6f0650ce06042cefc6ac703d0d7ab6c7a917289f2539ad4"}, - {file = "aiohttp-3.13.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9af5e68ee47d6534d36791bbe9b646d2a7c7deb6fc24d7943628edfbb3581f29"}, - {file = "aiohttp-3.13.3-cp311-cp311-win32.whl", hash = "sha256:a2212ad43c0833a873d0fb3c63fa1bacedd4cf6af2fee62bf4b739ceec3ab239"}, - {file = "aiohttp-3.13.3-cp311-cp311-win_amd64.whl", hash = "sha256:642f752c3eb117b105acbd87e2c143de710987e09860d674e068c4c2c441034f"}, - {file = "aiohttp-3.13.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b903a4dfee7d347e2d87697d0713be59e0b87925be030c9178c5faa58ea58d5c"}, - {file = "aiohttp-3.13.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a45530014d7a1e09f4a55f4f43097ba0fd155089372e105e4bff4ca76cb1b168"}, - {file = "aiohttp-3.13.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27234ef6d85c914f9efeb77ff616dbf4ad2380be0cda40b4db086ffc7ddd1b7d"}, - {file = "aiohttp-3.13.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d32764c6c9aafb7fb55366a224756387cd50bfa720f32b88e0e6fa45b27dcf29"}, - {file = "aiohttp-3.13.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b1a6102b4d3ebc07dad44fbf07b45bb600300f15b552ddf1851b5390202ea2e3"}, - {file = "aiohttp-3.13.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c014c7ea7fb775dd015b2d3137378b7be0249a448a1612268b5a90c2d81de04d"}, - {file = "aiohttp-3.13.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2b8d8ddba8f95ba17582226f80e2de99c7a7948e66490ef8d947e272a93e9463"}, - {file = "aiohttp-3.13.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ae8dd55c8e6c4257eae3a20fd2c8f41edaea5992ed67156642493b8daf3cecc"}, - {file = "aiohttp-3.13.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:01ad2529d4b5035578f5081606a465f3b814c542882804e2e8cda61adf5c71bf"}, - {file = "aiohttp-3.13.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bb4f7475e359992b580559e008c598091c45b5088f28614e855e42d39c2f1033"}, - {file = "aiohttp-3.13.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c19b90316ad3b24c69cd78d5c9b4f3aa4497643685901185b65166293d36a00f"}, - {file = "aiohttp-3.13.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:96d604498a7c782cb15a51c406acaea70d8c027ee6b90c569baa6e7b93073679"}, - {file = "aiohttp-3.13.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:084911a532763e9d3dd95adf78a78f4096cd5f58cdc18e6fdbc1b58417a45423"}, - {file = "aiohttp-3.13.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7a4a94eb787e606d0a09404b9c38c113d3b099d508021faa615d70a0131907ce"}, - {file = "aiohttp-3.13.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87797e645d9d8e222e04160ee32aa06bc5c163e8499f24db719e7852ec23093a"}, - {file = "aiohttp-3.13.3-cp312-cp312-win32.whl", hash = "sha256:b04be762396457bef43f3597c991e192ee7da460a4953d7e647ee4b1c28e7046"}, - {file = "aiohttp-3.13.3-cp312-cp312-win_amd64.whl", hash = "sha256:e3531d63d3bdfa7e3ac5e9b27b2dd7ec9df3206a98e0b3445fa906f233264c57"}, - {file = "aiohttp-3.13.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5dff64413671b0d3e7d5918ea490bdccb97a4ad29b3f311ed423200b2203e01c"}, - {file = "aiohttp-3.13.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:87b9aab6d6ed88235aa2970294f496ff1a1f9adcd724d800e9b952395a80ffd9"}, - {file = "aiohttp-3.13.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:425c126c0dc43861e22cb1c14ba4c8e45d09516d0a3ae0a3f7494b79f5f233a3"}, - {file = "aiohttp-3.13.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f9120f7093c2a32d9647abcaf21e6ad275b4fbec5b55969f978b1a97c7c86bf"}, - {file = "aiohttp-3.13.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:697753042d57f4bf7122cab985bf15d0cef23c770864580f5af4f52023a56bd6"}, - {file = "aiohttp-3.13.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6de499a1a44e7de70735d0b39f67c8f25eb3d91eb3103be99ca0fa882cdd987d"}, - {file = "aiohttp-3.13.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:37239e9f9a7ea9ac5bf6b92b0260b01f8a22281996da609206a84df860bc1261"}, - {file = "aiohttp-3.13.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f76c1e3fe7d7c8afad7ed193f89a292e1999608170dcc9751a7462a87dfd5bc0"}, - {file = "aiohttp-3.13.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fc290605db2a917f6e81b0e1e0796469871f5af381ce15c604a3c5c7e51cb730"}, - {file = "aiohttp-3.13.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4021b51936308aeea0367b8f006dc999ca02bc118a0cc78c303f50a2ff6afb91"}, - {file = "aiohttp-3.13.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:49a03727c1bba9a97d3e93c9f93ca03a57300f484b6e935463099841261195d3"}, - {file = "aiohttp-3.13.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3d9908a48eb7416dc1f4524e69f1d32e5d90e3981e4e37eb0aa1cd18f9cfa2a4"}, - {file = "aiohttp-3.13.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2712039939ec963c237286113c68dbad80a82a4281543f3abf766d9d73228998"}, - {file = "aiohttp-3.13.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7bfdc049127717581866fa4708791220970ce291c23e28ccf3922c700740fdc0"}, - {file = "aiohttp-3.13.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8057c98e0c8472d8846b9c79f56766bcc57e3e8ac7bfd510482332366c56c591"}, - {file = "aiohttp-3.13.3-cp313-cp313-win32.whl", hash = "sha256:1449ceddcdbcf2e0446957863af03ebaaa03f94c090f945411b61269e2cb5daf"}, - {file = "aiohttp-3.13.3-cp313-cp313-win_amd64.whl", hash = "sha256:693781c45a4033d31d4187d2436f5ac701e7bbfe5df40d917736108c1cc7436e"}, - {file = "aiohttp-3.13.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:ea37047c6b367fd4bd632bff8077449b8fa034b69e812a18e0132a00fae6e808"}, - {file = "aiohttp-3.13.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6fc0e2337d1a4c3e6acafda6a78a39d4c14caea625124817420abceed36e2415"}, - {file = "aiohttp-3.13.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c685f2d80bb67ca8c3837823ad76196b3694b0159d232206d1e461d3d434666f"}, - {file = "aiohttp-3.13.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e377758516d262bde50c2584fc6c578af272559c409eecbdd2bae1601184d6"}, - {file = "aiohttp-3.13.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:34749271508078b261c4abb1767d42b8d0c0cc9449c73a4df494777dc55f0687"}, - {file = "aiohttp-3.13.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:82611aeec80eb144416956ec85b6ca45a64d76429c1ed46ae1b5f86c6e0c9a26"}, - {file = "aiohttp-3.13.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2fff83cfc93f18f215896e3a190e8e5cb413ce01553901aca925176e7568963a"}, - {file = "aiohttp-3.13.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bbe7d4cecacb439e2e2a8a1a7b935c25b812af7a5fd26503a66dadf428e79ec1"}, - {file = "aiohttp-3.13.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b928f30fe49574253644b1ca44b1b8adbd903aa0da4b9054a6c20fc7f4092a25"}, - {file = "aiohttp-3.13.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7b5e8fe4de30df199155baaf64f2fcd604f4c678ed20910db8e2c66dc4b11603"}, - {file = "aiohttp-3.13.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:8542f41a62bcc58fc7f11cf7c90e0ec324ce44950003feb70640fc2a9092c32a"}, - {file = "aiohttp-3.13.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5e1d8c8b8f1d91cd08d8f4a3c2b067bfca6ec043d3ff36de0f3a715feeedf926"}, - {file = "aiohttp-3.13.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:90455115e5da1c3c51ab619ac57f877da8fd6d73c05aacd125c5ae9819582aba"}, - {file = "aiohttp-3.13.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:042e9e0bcb5fba81886c8b4fbb9a09d6b8a00245fd8d88e4d989c1f96c74164c"}, - {file = "aiohttp-3.13.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2eb752b102b12a76ca02dff751a801f028b4ffbbc478840b473597fc91a9ed43"}, - {file = "aiohttp-3.13.3-cp314-cp314-win32.whl", hash = "sha256:b556c85915d8efaed322bf1bdae9486aa0f3f764195a0fb6ee962e5c71ef5ce1"}, - {file = "aiohttp-3.13.3-cp314-cp314-win_amd64.whl", hash = "sha256:9bf9f7a65e7aa20dd764151fb3d616c81088f91f8df39c3893a536e279b4b984"}, - {file = "aiohttp-3.13.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:05861afbbec40650d8a07ea324367cb93e9e8cc7762e04dd4405df99fa65159c"}, - {file = "aiohttp-3.13.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2fc82186fadc4a8316768d61f3722c230e2c1dcab4200d52d2ebdf2482e47592"}, - {file = "aiohttp-3.13.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0add0900ff220d1d5c5ebbf99ed88b0c1bbf87aa7e4262300ed1376a6b13414f"}, - {file = "aiohttp-3.13.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:568f416a4072fbfae453dcf9a99194bbb8bdeab718e08ee13dfa2ba0e4bebf29"}, - {file = "aiohttp-3.13.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:add1da70de90a2569c5e15249ff76a631ccacfe198375eead4aadf3b8dc849dc"}, - {file = "aiohttp-3.13.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b47b7ba335d2e9b1239fa571131a87e2d8ec96b333e68b2a305e7a98b0bae2"}, - {file = "aiohttp-3.13.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dd4dce1c718e38081c8f35f323209d4c1df7d4db4bab1b5c88a6b4d12b74587"}, - {file = "aiohttp-3.13.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34bac00a67a812570d4a460447e1e9e06fae622946955f939051e7cc895cfab8"}, - {file = "aiohttp-3.13.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a19884d2ee70b06d9204b2727a7b9f983d0c684c650254679e716b0b77920632"}, - {file = "aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ca7f2bb6ba8348a3614c7918cc4bb73268c5ac2a207576b7afea19d3d9f64"}, - {file = "aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:b0d95340658b9d2f11d9697f59b3814a9d3bb4b7a7c20b131df4bcef464037c0"}, - {file = "aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1e53262fd202e4b40b70c3aff944a8155059beedc8a89bba9dc1f9ef06a1b56"}, - {file = "aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d60ac9663f44168038586cab2157e122e46bdef09e9368b37f2d82d354c23f72"}, - {file = "aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:90751b8eed69435bac9ff4e3d2f6b3af1f57e37ecb0fbeee59c0174c9e2d41df"}, - {file = "aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fc353029f176fd2b3ec6cfc71be166aba1936fe5d73dd1992ce289ca6647a9aa"}, - {file = "aiohttp-3.13.3-cp314-cp314t-win32.whl", hash = "sha256:2e41b18a58da1e474a057b3d35248d8320029f61d70a37629535b16a0c8f3767"}, - {file = "aiohttp-3.13.3-cp314-cp314t-win_amd64.whl", hash = "sha256:44531a36aa2264a1860089ffd4dce7baf875ee5a6079d5fb42e261c704ef7344"}, - {file = "aiohttp-3.13.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:31a83ea4aead760dfcb6962efb1d861db48c34379f2ff72db9ddddd4cda9ea2e"}, - {file = "aiohttp-3.13.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:988a8c5e317544fdf0d39871559e67b6341065b87fceac641108c2096d5506b7"}, - {file = "aiohttp-3.13.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9b174f267b5cfb9a7dba9ee6859cecd234e9a681841eb85068059bc867fb8f02"}, - {file = "aiohttp-3.13.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:947c26539750deeaee933b000fb6517cc770bbd064bad6033f1cff4803881e43"}, - {file = "aiohttp-3.13.3-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9ebf57d09e131f5323464bd347135a88622d1c0976e88ce15b670e7ad57e4bd6"}, - {file = "aiohttp-3.13.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4ae5b5a0e1926e504c81c5b84353e7a5516d8778fbbff00429fe7b05bb25cbce"}, - {file = "aiohttp-3.13.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2ba0eea45eb5cc3172dbfc497c066f19c41bac70963ea1a67d51fc92e4cf9a80"}, - {file = "aiohttp-3.13.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bae5c2ed2eae26cc382020edad80d01f36cb8e746da40b292e68fec40421dc6a"}, - {file = "aiohttp-3.13.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8a60e60746623925eab7d25823329941aee7242d559baa119ca2b253c88a7bd6"}, - {file = "aiohttp-3.13.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e50a2e1404f063427c9d027378472316201a2290959a295169bcf25992d04558"}, - {file = "aiohttp-3.13.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:9a9dc347e5a3dc7dfdbc1f82da0ef29e388ddb2ed281bfce9dd8248a313e62b7"}, - {file = "aiohttp-3.13.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b46020d11d23fe16551466c77823df9cc2f2c1e63cc965daf67fa5eec6ca1877"}, - {file = "aiohttp-3.13.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:69c56fbc1993fa17043e24a546959c0178fe2b5782405ad4559e6c13975c15e3"}, - {file = "aiohttp-3.13.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:b99281b0704c103d4e11e72a76f1b543d4946fea7dd10767e7e1b5f00d4e5704"}, - {file = "aiohttp-3.13.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:40c5e40ecc29ba010656c18052b877a1c28f84344825efa106705e835c28530f"}, - {file = "aiohttp-3.13.3-cp39-cp39-win32.whl", hash = "sha256:56339a36b9f1fc708260c76c87e593e2afb30d26de9ae1eb445b5e051b98a7a1"}, - {file = "aiohttp-3.13.3-cp39-cp39-win_amd64.whl", hash = "sha256:c6b8568a3bb5819a0ad087f16d40e5a3fb6099f39ea1d5625a3edc1e923fc538"}, - {file = "aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88"}, + {file = "aiohttp-3.13.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:02222e7e233295f40e011c1b00e3b0bd451f22cf853a0304c3595633ee47da4b"}, + {file = "aiohttp-3.13.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bace460460ed20614fa6bc8cb09966c0b8517b8c58ad8046828c6078d25333b5"}, + {file = "aiohttp-3.13.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f546a4dc1e6a5edbb9fd1fd6ad18134550e096a5a43f4ad74acfbd834fc6670"}, + {file = "aiohttp-3.13.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c86969d012e51b8e415a8c6ce96f7857d6a87d6207303ab02d5d11ef0cad2274"}, + {file = "aiohttp-3.13.5-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b6f6cd1560c5fa427e3b6074bb24d2c64e225afbb7165008903bd42e4e33e28a"}, + {file = "aiohttp-3.13.5-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:636bc362f0c5bbc7372bc3ae49737f9e3030dbce469f0f422c8f38079780363d"}, + {file = "aiohttp-3.13.5-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6a7cbeb06d1070f1d14895eeeed4dac5913b22d7b456f2eb969f11f4b3993796"}, + {file = "aiohttp-3.13.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca9ef7517fd7874a1a08970ae88f497bf5c984610caa0bf40bd7e8450852b95"}, + {file = "aiohttp-3.13.5-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:019a67772e034a0e6b9b17c13d0a8fe56ad9fb150fc724b7f3ffd3724288d9e5"}, + {file = "aiohttp-3.13.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f34ecee82858e41dd217734f0c41a532bd066bcaab636ad830f03a30b2a96f2a"}, + {file = "aiohttp-3.13.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4eac02d9af4813ee289cd63a361576da36dba57f5a1ab36377bc2600db0cbb73"}, + {file = "aiohttp-3.13.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4beac52e9fe46d6abf98b0176a88154b742e878fdf209d2248e99fcdf73cd297"}, + {file = "aiohttp-3.13.5-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:c180f480207a9b2475f2b8d8bd7204e47aec952d084b2a2be58a782ffcf96074"}, + {file = "aiohttp-3.13.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2837fb92951564d6339cedae4a7231692aa9f73cbc4fb2e04263b96844e03b4e"}, + {file = "aiohttp-3.13.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9010032a0b9710f58012a1e9c222528763d860ba2ee1422c03473eab47703e7"}, + {file = "aiohttp-3.13.5-cp310-cp310-win32.whl", hash = "sha256:7c4b6668b2b2b9027f209ddf647f2a4407784b5d88b8be4efcc72036f365baf9"}, + {file = "aiohttp-3.13.5-cp310-cp310-win_amd64.whl", hash = "sha256:cd3db5927bf9167d5a6157ddb2f036f6b6b0ad001ac82355d43e97a4bde76d76"}, + {file = "aiohttp-3.13.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ab7229b6f9b5c1ba4910d6c41a9eb11f543eadb3f384df1b4c293f4e73d44d6"}, + {file = "aiohttp-3.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8f14c50708bb156b3a3ca7230b3d820199d56a48e3af76fa21c2d6087190fe3d"}, + {file = "aiohttp-3.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7d2f8616f0ff60bd332022279011776c3ac0faa0f1b463f7bb12326fbc97a1c"}, + {file = "aiohttp-3.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2567b72e1ffc3ab25510db43f355b29eeada56c0a622e58dcdb19530eb0a3cb"}, + {file = "aiohttp-3.13.5-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fb0540c854ac9c0c5ad495908fdfd3e332d553ec731698c0e29b1877ba0d2ec6"}, + {file = "aiohttp-3.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9883051c6972f58bfc4ebb2116345ee2aa151178e99c3f2b2bbe2af712abd13"}, + {file = "aiohttp-3.13.5-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2294172ce08a82fb7c7273485895de1fa1186cc8294cfeb6aef4af42ad261174"}, + {file = "aiohttp-3.13.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a807cabd5115fb55af198b98178997a5e0e57dead43eb74a93d9c07d6d4a7dc"}, + {file = "aiohttp-3.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aa6d0d932e0f39c02b80744273cd5c388a2d9bc07760a03164f229c8e02662f6"}, + {file = "aiohttp-3.13.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60869c7ac4aaabe7110f26499f3e6e5696eae98144735b12a9c3d9eae2b51a49"}, + {file = "aiohttp-3.13.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:26d2f8546f1dfa75efa50c3488215a903c0168d253b75fba4210f57ab77a0fb8"}, + {file = "aiohttp-3.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1162a1492032c82f14271e831c8f4b49f2b6078f4f5fc74de2c912fa225d51d"}, + {file = "aiohttp-3.13.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:8b14eb3262fad0dc2f89c1a43b13727e709504972186ff6a99a3ecaa77102b6c"}, + {file = "aiohttp-3.13.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ca9ac61ac6db4eb6c2a0cd1d0f7e1357647b638ccc92f7e9d8d133e71ed3c6ac"}, + {file = "aiohttp-3.13.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7996023b2ed59489ae4762256c8516df9820f751cf2c5da8ed2fb20ee50abab3"}, + {file = "aiohttp-3.13.5-cp311-cp311-win32.whl", hash = "sha256:77dfa48c9f8013271011e51c00f8ada19851f013cde2c48fca1ba5e0caf5bb06"}, + {file = "aiohttp-3.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:d3a4834f221061624b8887090637db9ad4f61752001eae37d56c52fddade2dc8"}, + {file = "aiohttp-3.13.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:023ecba036ddd840b0b19bf195bfae970083fd7024ce1ac22e9bba90464620e9"}, + {file = "aiohttp-3.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15c933ad7920b7d9a20de151efcd05a6e38302cbf0e10c9b2acb9a42210a2416"}, + {file = "aiohttp-3.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab2899f9fa2f9f741896ebb6fa07c4c883bfa5c7f2ddd8cf2aafa86fa981b2d2"}, + {file = "aiohttp-3.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60eaa2d440cd4707696b52e40ed3e2b0f73f65be07fd0ef23b6b539c9c0b0b4"}, + {file = "aiohttp-3.13.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55b3bdd3292283295774ab585160c4004f4f2f203946997f49aac032c84649e9"}, + {file = "aiohttp-3.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2b2355dc094e5f7d45a7bb262fe7207aa0460b37a0d87027dcf21b5d890e7d5"}, + {file = "aiohttp-3.13.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b38765950832f7d728297689ad78f5f2cf79ff82487131c4d26fe6ceecdc5f8e"}, + {file = "aiohttp-3.13.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b18f31b80d5a33661e08c89e202edabf1986e9b49c42b4504371daeaa11b47c1"}, + {file = "aiohttp-3.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:33add2463dde55c4f2d9635c6ab33ce154e5ecf322bd26d09af95c5f81cfa286"}, + {file = "aiohttp-3.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:327cc432fdf1356fb4fbc6fe833ad4e9f6aacb71a8acaa5f1855e4b25910e4a9"}, + {file = "aiohttp-3.13.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7c35b0bf0b48a70b4cb4fc5d7bed9b932532728e124874355de1a0af8ec4bc88"}, + {file = "aiohttp-3.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:df23d57718f24badef8656c49743e11a89fd6f5358fa8a7b96e728fda2abf7d3"}, + {file = "aiohttp-3.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:02e048037a6501a5ec1f6fc9736135aec6eb8a004ce48838cb951c515f32c80b"}, + {file = "aiohttp-3.13.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31cebae8b26f8a615d2b546fee45d5ffb76852ae6450e2a03f42c9102260d6fe"}, + {file = "aiohttp-3.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:888e78eb5ca55a615d285c3c09a7a91b42e9dd6fc699b166ebd5dee87c9ccf14"}, + {file = "aiohttp-3.13.5-cp312-cp312-win32.whl", hash = "sha256:8bd3ec6376e68a41f9f95f5ed170e2fcf22d4eb27a1f8cb361d0508f6e0557f3"}, + {file = "aiohttp-3.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:110e448e02c729bcebb18c60b9214a87ba33bac4a9fa5e9a5f139938b56c6cb1"}, + {file = "aiohttp-3.13.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5029cc80718bbd545123cd8fe5d15025eccaaaace5d0eeec6bd556ad6163d61"}, + {file = "aiohttp-3.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4bb6bf5811620003614076bdc807ef3b5e38244f9d25ca5fe888eaccea2a9832"}, + {file = "aiohttp-3.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a84792f8631bf5a94e52d9cc881c0b824ab42717165a5579c760b830d9392ac9"}, + {file = "aiohttp-3.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57653eac22c6a4c13eb22ecf4d673d64a12f266e72785ab1c8b8e5940d0e8090"}, + {file = "aiohttp-3.13.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5e5f7debc7a57af53fdf5c5009f9391d9f4c12867049d509bf7bb164a6e295b"}, + {file = "aiohttp-3.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c719f65bebcdf6716f10e9eff80d27567f7892d8988c06de12bbbd39307c6e3a"}, + {file = "aiohttp-3.13.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d97f93fdae594d886c5a866636397e2bcab146fd7a132fd6bb9ce182224452f8"}, + {file = "aiohttp-3.13.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3df334e39d4c2f899a914f1dba283c1aadc311790733f705182998c6f7cae665"}, + {file = "aiohttp-3.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe6970addfea9e5e081401bcbadf865d2b6da045472f58af08427e108d618540"}, + {file = "aiohttp-3.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7becdf835feff2f4f335d7477f121af787e3504b48b449ff737afb35869ba7bb"}, + {file = "aiohttp-3.13.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:676e5651705ad5d8a70aeb8eb6936c436d8ebbd56e63436cb7dd9bb36d2a9a46"}, + {file = "aiohttp-3.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9b16c653d38eb1a611cc898c41e76859ca27f119d25b53c12875fd0474ae31a8"}, + {file = "aiohttp-3.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:999802d5fa0389f58decd24b537c54aa63c01c3219ce17d1214cbda3c2b22d2d"}, + {file = "aiohttp-3.13.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ec707059ee75732b1ba130ed5f9580fe10ff75180c812bc267ded039db5128c6"}, + {file = "aiohttp-3.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d6d44a5b48132053c2f6cd5c8cb14bc67e99a63594e336b0f2af81e94d5530c"}, + {file = "aiohttp-3.13.5-cp313-cp313-win32.whl", hash = "sha256:329f292ed14d38a6c4c435e465f48bebb47479fd676a0411936cc371643225cc"}, + {file = "aiohttp-3.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:69f571de7500e0557801c0b51f4780482c0ec5fe2ac851af5a92cfce1af1cb83"}, + {file = "aiohttp-3.13.5-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:eb4639f32fd4a9904ab8fb45bf3383ba71137f3d9d4ba25b3b3f3109977c5b8c"}, + {file = "aiohttp-3.13.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:7e5dc4311bd5ac493886c63cbf76ab579dbe4641268e7c74e48e774c74b6f2be"}, + {file = "aiohttp-3.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:756c3c304d394977519824449600adaf2be0ccee76d206ee339c5e76b70ded25"}, + {file = "aiohttp-3.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecc26751323224cf8186efcf7fbcbc30f4e1d8c7970659daf25ad995e4032a56"}, + {file = "aiohttp-3.13.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10a75acfcf794edf9d8db50e5a7ec5fc818b2a8d3f591ce93bc7b1210df016d2"}, + {file = "aiohttp-3.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f7a18f258d124cd678c5fe072fe4432a4d5232b0657fca7c1847f599233c83a"}, + {file = "aiohttp-3.13.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:df6104c009713d3a89621096f3e3e88cc323fd269dbd7c20afe18535094320be"}, + {file = "aiohttp-3.13.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:241a94f7de7c0c3b616627aaad530fe2cb620084a8b144d3be7b6ecfe95bae3b"}, + {file = "aiohttp-3.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c974fb66180e58709b6fc402846f13791240d180b74de81d23913abe48e96d94"}, + {file = "aiohttp-3.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6e27ea05d184afac78aabbac667450c75e54e35f62238d44463131bd3f96753d"}, + {file = "aiohttp-3.13.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a79a6d399cef33a11b6f004c67bb07741d91f2be01b8d712d52c75711b1e07c7"}, + {file = "aiohttp-3.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c632ce9c0b534fbe25b52c974515ed674937c5b99f549a92127c85f771a78772"}, + {file = "aiohttp-3.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fceedde51fbd67ee2bcc8c0b33d0126cc8b51ef3bbde2f86662bd6d5a6f10ec5"}, + {file = "aiohttp-3.13.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f92995dfec9420bb69ae629abf422e516923ba79ba4403bc750d94fb4a6c68c1"}, + {file = "aiohttp-3.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20ae0ff08b1f2c8788d6fb85afcb798654ae6ba0b747575f8562de738078457b"}, + {file = "aiohttp-3.13.5-cp314-cp314-win32.whl", hash = "sha256:b20df693de16f42b2472a9c485e1c948ee55524786a0a34345511afdd22246f3"}, + {file = "aiohttp-3.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:f85c6f327bf0b8c29da7d93b1cabb6363fb5e4e160a32fa241ed2dce21b73162"}, + {file = "aiohttp-3.13.5-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:1efb06900858bb618ff5cee184ae2de5828896c448403d51fb633f09e109be0a"}, + {file = "aiohttp-3.13.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fee86b7c4bd29bdaf0d53d14739b08a106fdda809ca5fe032a15f52fae5fe254"}, + {file = "aiohttp-3.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:20058e23909b9e65f9da62b396b77dfa95965cbe840f8def6e572538b1d32e36"}, + {file = "aiohttp-3.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cf20a8d6868cb15a73cab329ffc07291ba8c22b1b88176026106ae39aa6df0f"}, + {file = "aiohttp-3.13.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:330f5da04c987f1d5bdb8ae189137c77139f36bd1cb23779ca1a354a4b027800"}, + {file = "aiohttp-3.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f1cbf0c7926d315c3c26c2da41fd2b5d2fe01ac0e157b78caefc51a782196cf"}, + {file = "aiohttp-3.13.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:53fc049ed6390d05423ba33103ded7281fe897cf97878f369a527070bd95795b"}, + {file = "aiohttp-3.13.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:898703aa2667e3c5ca4c54ca36cd73f58b7a38ef87a5606414799ebce4d3fd3a"}, + {file = "aiohttp-3.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0494a01ca9584eea1e5fbd6d748e61ecff218c51b576ee1999c23db7066417d8"}, + {file = "aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6cf81fe010b8c17b09495cbd15c1d35afbc8fb405c0c9cf4738e5ae3af1d65be"}, + {file = "aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c564dd5f09ddc9d8f2c2d0a301cd30a79a2cc1b46dd1a73bef8f0038863d016b"}, + {file = "aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2994be9f6e51046c4f864598fd9abeb4fba6e88f0b2152422c9666dcd4aea9c6"}, + {file = "aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:157826e2fa245d2ef46c83ea8a5faf77ca19355d278d425c29fda0beb3318037"}, + {file = "aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a8aca50daa9493e9e13c0f566201a9006f080e7c50e5e90d0b06f53146a54500"}, + {file = "aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3b13560160d07e047a93f23aaa30718606493036253d5430887514715b67c9d9"}, + {file = "aiohttp-3.13.5-cp314-cp314t-win32.whl", hash = "sha256:9a0f4474b6ea6818b41f82172d799e4b3d29e22c2c520ce4357856fced9af2f8"}, + {file = "aiohttp-3.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:18a2f6c1182c51baa1d28d68fea51513cb2a76612f038853c0ad3c145423d3d9"}, + {file = "aiohttp-3.13.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:347542f0ea3f95b2a955ee6656461fa1c776e401ac50ebce055a6c38454a0adf"}, + {file = "aiohttp-3.13.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:178c7b5e62b454c2bc790786e6058c3cc968613b4419251b478c153a4aec32b1"}, + {file = "aiohttp-3.13.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af545c2cffdb0967a96b6249e6f5f7b0d92cdfd267f9d5238d5b9ca63e8edb10"}, + {file = "aiohttp-3.13.5-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:206b7b3ef96e4ce211754f0cd003feb28b7d81f0ad26b8d077a5d5161436067f"}, + {file = "aiohttp-3.13.5-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ee5e86776273de1795947d17bddd6bb19e0365fd2af4289c0d2c5454b6b1d36b"}, + {file = "aiohttp-3.13.5-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:95d14ca7abefde230f7639ec136ade282655431fd5db03c343b19dda72dd1643"}, + {file = "aiohttp-3.13.5-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:912d4b6af530ddb1338a66229dac3a25ff11d4448be3ec3d6340583995f56031"}, + {file = "aiohttp-3.13.5-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e999f0c88a458c836d5fb521814e92ed2172c649200336a6df514987c1488258"}, + {file = "aiohttp-3.13.5-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:39380e12bd1f2fdab4285b6e055ad48efbaed5c836433b142ed4f5b9be71036a"}, + {file = "aiohttp-3.13.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9efcc0f11d850cefcafdd9275b9576ad3bfb539bed96807663b32ad99c4d4b88"}, + {file = "aiohttp-3.13.5-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:147b4f501d0292077f29d5268c16bb7c864a1f054d7001c4c1812c0421ea1ed0"}, + {file = "aiohttp-3.13.5-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d147004fede1b12f6013a6dbb2a26a986a671a03c6ea740ddc76500e5f1c399f"}, + {file = "aiohttp-3.13.5-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:9277145d36a01653863899c665243871434694bcc3431922c3b35c978061bdb8"}, + {file = "aiohttp-3.13.5-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4e704c52438f66fdd89588346183d898bb42167cf88f8b7ff1c0f9fc957c348f"}, + {file = "aiohttp-3.13.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a8a4d3427e8de1312ddf309cc482186466c79895b3a139fed3259fc01dfa9a5b"}, + {file = "aiohttp-3.13.5-cp39-cp39-win32.whl", hash = "sha256:6f497a6876aa4b1a102b04996ce4c1170c7040d83faa9387dd921c16e30d5c83"}, + {file = "aiohttp-3.13.5-cp39-cp39-win_amd64.whl", hash = "sha256:cb979826071c0986a5f08333a36104153478ce6018c58cba7f9caddaf63d5d67"}, + {file = "aiohttp-3.13.5.tar.gz", hash = "sha256:9d98cc980ecc96be6eb4c1994ce35d28d8b1f5e5208a23b421187d1209dbb7d1"}, ] [package.dependencies] From 6c2beba8a30b6d4ffe8f32e84e259e8a28afbc7b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 11:03:10 +0200 Subject: [PATCH 264/264] chore(deps): bump requests from 2.33.0 to 2.33.1 (#303) Bumps [requests](https://github.com/psf/requests) from 2.33.0 to 2.33.1. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.33.0...v2.33.1) --- updated-dependencies: - dependency-name: requests dependency-version: 2.33.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 1520948..de4b40e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1734,14 +1734,14 @@ md = ["cmarkgfm (>=0.8.0)"] [[package]] name = "requests" -version = "2.33.0" +version = "2.33.1" description = "Python HTTP for Humans." optional = false python-versions = ">=3.10" groups = ["main", "dev"] files = [ - {file = "requests-2.33.0-py3-none-any.whl", hash = "sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b"}, - {file = "requests-2.33.0.tar.gz", hash = "sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652"}, + {file = "requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a"}, + {file = "requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517"}, ] [package.dependencies] @@ -1752,7 +1752,6 @@ urllib3 = ">=1.26,<3" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] -test = ["PySocks (>=1.5.6,!=1.5.7)", "pytest (>=3)", "pytest-cov", "pytest-httpbin (==2.1.0)", "pytest-mock", "pytest-xdist"] use-chardet-on-py3 = ["chardet (>=3.0.2,<8)"] [[package]]