From 77c199998cfadc212443a8c1c7c35959182435bb Mon Sep 17 00:00:00 2001 From: zhiren Date: Wed, 14 Jun 2023 15:53:38 -0400 Subject: [PATCH 01/12] update to next version --- .github/workflows/build-and-publish.yml | 19 ++++++------------- app/resources/custom_help.py | 2 +- .../dataset_manager/dataset_download.py | 1 + pyproject.toml | 2 +- 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml index 6554e157..8ab440fe 100644 --- a/.github/workflows/build-and-publish.yml +++ b/.github/workflows/build-and-publish.yml @@ -64,31 +64,24 @@ jobs: - name: Build binary run: poetry run pyinstaller -F --distpath ./app/bundled_app/linux --specpath ./app/build/linux --workpath ./app/build/linux --paths=./.venv/lib/python3.9/site-packages ./app/pilotcli.py -n ${{ github.sha }} + - name: Rename output file + run: mv "./app/bundled_app/linux/${{ github.sha }}" "./app/bundled_app/linux/pilotcli_linux" + - name: Set version in env run: poetry run echo "TAG_VERSION=`poetry version --short`" >> $GITHUB_ENV - name: Create Release id: create_release - uses: actions/create-release@v1 + uses: softprops/action-gh-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token with: tag_name: ${{ env.TAG_VERSION }} - release_name: Release ${{ needs.extract-branch-name.outputs.branch }} ${{ env.TAG_VERSION }} + name: Release ${{ needs.extract-branch-name.outputs.branch }} ${{ env.TAG_VERSION }} body: ${{ github.event.head_commit.message }} draft: false prerelease: false - - - name: Upload Release Binary - id: upload-release-binary - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps - asset_path: ./app/bundled_app/linux/${{ github.sha }} - asset_name: pilotcli_linux - asset_content_type: application/octet-stream + files: ./app/bundled_app/linux/pilotcli_linux push-binary-macos: needs: [ push-binary-linux ] diff --git a/app/resources/custom_help.py b/app/resources/custom_help.py index 5906ad1b..0da765ba 100644 --- a/app/resources/custom_help.py +++ b/app/resources/custom_help.py @@ -6,7 +6,7 @@ class HelpPage: page = { 'update': { - 'version': '2.5.0', + 'version': '2.5.1', '1': 'The logic of normal upload and resumble are splited. ' 'add new command for resumable upload as `pilotcli file resume -r manifest.json`', '2': 'The manifest file will be output for both file/folder upload', diff --git a/app/services/dataset_manager/dataset_download.py b/app/services/dataset_manager/dataset_download.py index 5a60bdd1..6ee02c64 100644 --- a/app/services/dataset_manager/dataset_download.py +++ b/app/services/dataset_manager/dataset_download.py @@ -68,6 +68,7 @@ def pre_dataset_download(self) -> Dict[str, Any]: response = resilient_session().post(url, headers=headers, json=payload) res = response.json() return res + except Exception as e: SrvErrorHandler.default_handle(f'error when pre dataset download:{e}', True) diff --git a/pyproject.toml b/pyproject.toml index 44e8d05c..2e6eed7f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "app" -version = "2.6.0a0" +version = "2.5.2a0" description = "This service is designed to support pilot platform" authors = ["Indoc Research"] From a655d2e390b1e49e04941cb248a2fd8b2f7543b2 Mon Sep 17 00:00:00 2001 From: Color Zhan Date: Fri, 30 Jun 2023 09:33:27 -0400 Subject: [PATCH 02/12] Pilot 3209: fixup the manifest attaching when resumable upload (#69) * fixup the resumable upload will not attach the file attribute * add test cases * bumpup version --------- Co-authored-by: zhiren --- app/commands/file.py | 9 ++++++- .../file_manager/file_upload/file_upload.py | 1 + .../file_manager/file_upload/upload_client.py | 3 +++ pyproject.toml | 2 +- tests/app/commands/test_file.py | 25 ++++++++++++++++++- 5 files changed, 37 insertions(+), 3 deletions(-) diff --git a/app/commands/file.py b/app/commands/file.py index acd53a0a..c5c73177 100644 --- a/app/commands/file.py +++ b/app/commands/file.py @@ -245,9 +245,16 @@ def file_resume(**kwargs): # noqa: C901 # are rather similar with the input validate_upload_event(resumable_manifest) - # print(resumable_manifest) resume_upload(resumable_manifest, thread) + # since only file upload can attach manifest, take the first file object + srv_manifest = SrvFileManifests() + item_id = next(iter(resumable_manifest.get('file_objects'))) + attribute = resumable_manifest.get('attributes') + zone = resumable_manifest.get('zone') + srv_manifest.attach_manifest(attribute, item_id, zone) if attribute else None + message_handler.SrvOutPutHandler.all_file_uploaded() + def validate_upload_event(event): """validate upload request, raise error when filed.""" diff --git a/app/services/file_manager/file_upload/file_upload.py b/app/services/file_manager/file_upload/file_upload.py index ee694a90..1e8814c0 100644 --- a/app/services/file_manager/file_upload/file_upload.py +++ b/app/services/file_manager/file_upload/file_upload.py @@ -156,6 +156,7 @@ def simple_upload( # noqa: C901 tags=tags, source_id=source_id, upload_message=upload_message, + attributes=attribute, ) # format the local path into object storage path for preupload diff --git a/app/services/file_manager/file_upload/upload_client.py b/app/services/file_manager/file_upload/upload_client.py index 57a93ab1..f4c42b88 100644 --- a/app/services/file_manager/file_upload/upload_client.py +++ b/app/services/file_manager/file_upload/upload_client.py @@ -55,6 +55,7 @@ def __init__( regular_file: str = True, tags: list = None, source_id: str = '', + attributes: dict = None, ): self.user = UserConfig() self.operator = self.user.username @@ -80,6 +81,7 @@ def __init__( # tags and souce_id are only allowed in file uplaod self.tags = tags self.source_id = source_id + self.attributes = attributes # the flag to indicate if all upload process finished # then the token refresh loop will end @@ -226,6 +228,7 @@ def output_manifest(self, file_objects: List[FileObject], output_path: str) -> D 'tags': self.tags, 'upload_message': self.upload_message, 'file_objects': {file_object.item_id: file_object.to_dict() for file_object in file_objects}, + 'attributes': self.attributes if self.attributes else {}, } with open(output_path, 'w') as f: diff --git a/pyproject.toml b/pyproject.toml index 2e6eed7f..b223a8bc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "app" -version = "2.5.2a0" +version = "2.5.2" description = "This service is designed to support pilot platform" authors = ["Indoc Research"] diff --git a/tests/app/commands/test_file.py b/tests/app/commands/test_file.py index f9939b79..e3aa4721 100644 --- a/tests/app/commands/test_file.py +++ b/tests/app/commands/test_file.py @@ -49,11 +49,34 @@ def test_resumable_upload_command_success(mocker, cli_runner): # mock the open function mocked_open_data = mocker.mock_open(read_data='test') mocker.patch('builtins.open', mocked_open_data) - mocker.patch('json.load', return_value={'resumable_manifest': 'test.json', 'thread': 1}) + mocker.patch('json.load', return_value={'file_objects': {'test_item_id': {'file_name': 'test.json'}}, 'zone': 1}) mocker.patch('app.commands.file.resume_upload', return_value=None) + result = cli_runner.invoke(file_resume, ['--resumable-manifest', 'test.json', '--thread', 1]) + assert result.exit_code == 0 + + +def test_resumable_upload_command_with_file_attribute_success(mocker, cli_runner): + mocker.patch('os.path.exists', return_value=True) + # mock the open function + mocked_open_data = mocker.mock_open(read_data='test') + mocker.patch('builtins.open', mocked_open_data) + mocker.patch( + 'json.load', + return_value={ + 'file_objects': {'test_item_id': {'file_name': 'test.json'}}, + 'zone': 1, + 'attributes': {'M1': {'attr1': '1'}}, + }, + ) + mocker.patch('app.commands.file.resume_upload', return_value=None) + + attribute_fun_mock = mocker.patch( + 'app.services.file_manager.file_manifests.SrvFileManifests.attach_manifest', return_value=None + ) result = cli_runner.invoke(file_resume, ['--resumable-manifest', 'test.json', '--thread', 1]) assert result.exit_code == 0 + attribute_fun_mock.assert_called_once() def test_resumable_upload_command_failed_with_file_not_exists(mocker, cli_runner): From c4c11273dffcd6a23e6a7c9cdbda874d4c9bb5bc Mon Sep 17 00:00:00 2001 From: Color Zhan Date: Fri, 7 Jul 2023 10:03:51 -0400 Subject: [PATCH 03/12] update the config creation logic to avoid the injection (#70) Co-authored-by: zhiren --- app/configs/user_config.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/configs/user_config.py b/app/configs/user_config.py index 6ecd0d6b..3893de1c 100644 --- a/app/configs/user_config.py +++ b/app/configs/user_config.py @@ -5,6 +5,7 @@ import configparser import os import time +from pathlib import Path from app.configs.app_config import AppConfig from app.models.singleton import Singleton @@ -24,7 +25,7 @@ def __init__(self): if not os.path.exists(AppConfig.Env.user_config_path): os.makedirs(AppConfig.Env.user_config_path) if not os.path.exists(AppConfig.Env.user_config_file): - os.system(r'touch {}'.format(AppConfig.Env.user_config_file)) + Path.touch(Path(AppConfig.Env.user_config_file)) self.config = configparser.ConfigParser() self.config.read(AppConfig.Env.user_config_file) if not self.config.has_section('USER'): From e2cf0d20d15ec4a66b188a30be8ee1735a6ec014 Mon Sep 17 00:00:00 2001 From: Vadym Moshynskyi Date: Mon, 10 Jul 2023 13:32:19 +0200 Subject: [PATCH 04/12] PILOT-3313: Add permissions check for config folder and file (#71) --- app/configs/app_config.py | 2 +- app/configs/user_config.py | 61 ++++++++++++++-- app/resources/custom_error.py | 1 + app/services/output_manager/error_handler.py | 1 + tests/app/configs/__init__.py | 3 + tests/app/configs/test_user_config.py | 75 ++++++++++++++++++++ tests/conftest.py | 2 +- 7 files changed, 136 insertions(+), 9 deletions(-) create mode 100644 tests/app/configs/__init__.py create mode 100644 tests/app/configs/test_user_config.py diff --git a/app/configs/app_config.py b/app/configs/app_config.py index 964c7f8c..52646fe4 100644 --- a/app/configs/app_config.py +++ b/app/configs/app_config.py @@ -11,7 +11,7 @@ class Env: project = ConfigClass.project user_config_path = ConfigClass.config_path msg_path = ConfigClass.custom_path - user_config_file = f'{user_config_path}/config.ini' + user_config_file = 'config.ini' token_warn_need_refresh = 120 # refresh token if token is about to expire token_refresh_interval = 90 # auto refresh token every 40 seconds diff --git a/app/configs/user_config.py b/app/configs/user_config.py index 3893de1c..7b417553 100644 --- a/app/configs/user_config.py +++ b/app/configs/user_config.py @@ -4,14 +4,19 @@ import configparser import os +import stat import time from pathlib import Path +from typing import Iterable +from typing import Union from app.configs.app_config import AppConfig from app.models.singleton import Singleton from app.services.crypto.crypto import decryption from app.services.crypto.crypto import encryption from app.services.crypto.crypto import generate_secret +from app.services.output_manager.error_handler import ECustomizedError +from app.services.output_manager.error_handler import SrvErrorHandler class UserConfig(metaclass=Singleton): @@ -21,13 +26,35 @@ class UserConfig(metaclass=Singleton): This user config is global. """ - def __init__(self): - if not os.path.exists(AppConfig.Env.user_config_path): - os.makedirs(AppConfig.Env.user_config_path) - if not os.path.exists(AppConfig.Env.user_config_file): - Path.touch(Path(AppConfig.Env.user_config_file)) + def __init__(self, config_path: Union[str, Path, None] = None, config_filename: Union[str, None] = None) -> None: + if config_path is None: + config_path = AppConfig.Env.user_config_path + if config_filename is None: + config_filename = AppConfig.Env.user_config_file + + config_path = Path(config_path) + if not config_path.exists(): + config_path.mkdir(mode=0o0700, exist_ok=False) + + current_user_id = os.geteuid() + + error = self._check_user_permissions(config_path, current_user_id, (0o0500, 0o0700)) + if error: + SrvErrorHandler.customized_handle(ECustomizedError.CONFIG_INVALID_PERMISSIONS, True, error) + return + + config_file = config_path / config_filename + if not config_file.exists(): + config_file.touch(mode=0o0600, exist_ok=False) + + error = self._check_user_permissions(config_file, current_user_id, (0o0400, 0o0600)) + if error: + SrvErrorHandler.customized_handle(ECustomizedError.CONFIG_INVALID_PERMISSIONS, True, error) + return + + self.config_file = config_file self.config = configparser.ConfigParser() - self.config.read(AppConfig.Env.user_config_file) + self.config.read(self.config_file) if not self.config.has_section('USER'): self.config['USER'] = { 'username': '', @@ -42,8 +69,28 @@ def __init__(self): } self.save() + def _check_user_permissions(self, path: Path, expected_uid: int, expected_bits: Iterable[int]) -> Union[str, None]: + """Check if file or folder is owned by the user and has proper access mode.""" + + path_stat = path.stat() + + path_uid = path_stat.st_uid + if path_uid != expected_uid: + return f'"{path}" is owned by the user id {path_uid}. Expected user id is {expected_uid}.' + + path_protection_bits = stat.S_IMODE(path_stat.st_mode) + if path_protection_bits not in expected_bits: + existing_permissions = oct(path_protection_bits).replace('0o', '') + expected_permissions = ', '.join(map(oct, expected_bits)).replace('0o', '') + return ( + f'Permissions {existing_permissions} for "{path}" are too open. ' + f'Expected permissions are {expected_permissions}.' + ) + + return None + def save(self): - with open(AppConfig.Env.user_config_file, 'w') as configfile: + with open(self.config_file, 'w') as configfile: self.config.write(configfile) def clear(self): diff --git a/app/resources/custom_error.py b/app/resources/custom_error.py index ed47b7be..bb093d1c 100644 --- a/app/resources/custom_error.py +++ b/app/resources/custom_error.py @@ -119,6 +119,7 @@ class Error: 'This cli has been configured already.' 'If you want to re-config this cli please remove previous file first' ), + 'CONFIG_INVALID_PERMISSIONS': 'Cannot proceed with current config permissions.\n%s', 'CONTAINER_REGISTRY_NO_URL': ( 'Container registry has not yet been configured. Related commands cannot be used at this time.' ), diff --git a/app/services/output_manager/error_handler.py b/app/services/output_manager/error_handler.py index 4f9ea3f4..bf1d02ae 100644 --- a/app/services/output_manager/error_handler.py +++ b/app/services/output_manager/error_handler.py @@ -81,6 +81,7 @@ class ECustomizedError(enum.Enum): CONTAINER_REGISTRY_NO_URL = 'CONTAINER_REGISTRY_NO_URL' CONFIG_NOT_FOUND = 'CONFIG_NOT_FOUND' CONFIG_EXIST = 'CONFIG_EXIST' + CONFIG_INVALID_PERMISSIONS = 'CONFIG_INVALID_PERMISSIONS' def customized_error_msg(customized_error: ECustomizedError): diff --git a/tests/app/configs/__init__.py b/tests/app/configs/__init__.py new file mode 100644 index 00000000..950d1b40 --- /dev/null +++ b/tests/app/configs/__init__.py @@ -0,0 +1,3 @@ +# Copyright (C) 2023 Indoc Research +# +# Contact Indoc Research for any questions regarding the use of this source code. diff --git a/tests/app/configs/test_user_config.py b/tests/app/configs/test_user_config.py new file mode 100644 index 00000000..24a483be --- /dev/null +++ b/tests/app/configs/test_user_config.py @@ -0,0 +1,75 @@ +# Copyright (C) 2023 Indoc Research +# +# Contact Indoc Research for any questions regarding the use of this source code. + +import os +import stat + +import pytest + +from app.configs.user_config import UserConfig + + +@pytest.fixture +def error_log(mocker): + return mocker.patch('app.services.logger_services.log_functions.error') + + +class TestUserConfig: + def test__init__creates_config_folder_with_0700_and_file_with_0600_access_modes(self, tmp_path, fake): + config_folder = tmp_path / fake.pystr() + file_name = fake.pystr() + + UserConfig(config_folder, file_name) + + config_folder_mode = stat.S_IMODE(config_folder.stat().st_mode) + assert config_folder_mode == 0o0700 + + config_file_mode = stat.S_IMODE((config_folder / file_name).stat().st_mode) + assert config_file_mode == 0o0600 + + def test__init__exits_with_error_when_config_folder_does_not_belong_to_user(self, error_log): + with pytest.raises(SystemExit): + UserConfig('/') + + expected_message = ( + 'Cannot proceed with current config permissions.\n' + f'"/" is owned by the user id 0. Expected user id is {os.geteuid()}.' + ) + + error_log.assert_called_with(expected_message) + + def test__init__exits_with_error_when_config_folder_does_not_have_expected_access_mode( + self, error_log, tmp_path, fake + ): + config_folder = tmp_path / fake.pystr() + config_folder.mkdir(mode=0o0755) + + with pytest.raises(SystemExit): + UserConfig(config_folder) + + expected_message = ( + 'Cannot proceed with current config permissions.\n' + f'Permissions 755 for "{config_folder}" are too open. Expected permissions are 500, 700.' + ) + + error_log.assert_called_with(expected_message) + + def test__init__exits_with_error_when_config_file_does_not_have_expected_access_mode( + self, error_log, tmp_path, fake + ): + config_folder = tmp_path / fake.pystr() + file_name = fake.pystr() + config_file = config_folder / file_name + config_folder.mkdir(mode=0o0700) + config_file.touch(mode=0o0644) + + with pytest.raises(SystemExit): + UserConfig(config_folder, file_name) + + expected_message = ( + 'Cannot proceed with current config permissions.\n' + f'Permissions 644 for "{config_file}" are too open. Expected permissions are 400, 600.' + ) + + error_log.assert_called_with(expected_message) diff --git a/tests/conftest.py b/tests/conftest.py index ca3f66de..ea0c6b85 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -13,7 +13,7 @@ @pytest.fixture(autouse=True) def reset_singletons(): - Singleton._instance = {} + Singleton._instances = {} @pytest.fixture(autouse=True) From 1c84e5a1486cab52b891cf78d990a0eac5e87176 Mon Sep 17 00:00:00 2001 From: Vadym Moshynskyi Date: Thu, 13 Jul 2023 10:49:01 +0200 Subject: [PATCH 05/12] PILOT-3305: Remove hpc command functionality (#72) --- app/commands/entry_point.py | 24 --- app/commands/hpc.py | 157 ------------------ app/configs/user_config.py | 10 -- app/models/convert_type.py | 15 -- app/models/service_meta_class.py | 7 - app/models/upload_form.py | 92 ---------- app/resources/custom_error.py | 2 - app/resources/custom_help.py | 13 -- .../file_manager/file_upload/upload_client.py | 4 +- app/services/hpc_manager/hpc_auth.py | 29 ---- app/services/hpc_manager/hpc_cluster.py | 123 -------------- app/services/hpc_manager/hpc_jobs.py | 66 -------- app/services/output_manager/error_handler.py | 2 - app/services/output_manager/help_page.py | 19 --- .../output_manager/response_handler.py | 73 -------- .../user_authentication/user_login_logout.py | 2 - app/utils/aggregated.py | 5 - .../app/services/hpc_manager/test_hpc_auth.py | 34 ---- .../services/hpc_manager/test_hpc_cluster.py | 55 ------ tests/conftest.py | 1 - 20 files changed, 2 insertions(+), 731 deletions(-) delete mode 100644 app/commands/hpc.py delete mode 100644 app/models/convert_type.py delete mode 100644 app/services/hpc_manager/hpc_auth.py delete mode 100644 app/services/hpc_manager/hpc_cluster.py delete mode 100644 app/services/hpc_manager/hpc_jobs.py delete mode 100644 app/services/output_manager/response_handler.py delete mode 100644 tests/app/services/hpc_manager/test_hpc_auth.py delete mode 100644 tests/app/services/hpc_manager/test_hpc_cluster.py diff --git a/app/commands/entry_point.py b/app/commands/entry_point.py index 0d65a6e4..a16da236 100644 --- a/app/commands/entry_point.py +++ b/app/commands/entry_point.py @@ -25,27 +25,17 @@ from .file import file_resume # Import custom commands -from .hpc import hpc_auth -from .hpc import hpc_get_node -from .hpc import hpc_get_partition -from .hpc import hpc_job_info -from .hpc import hpc_job_submit -from .hpc import hpc_list_nodes -from .hpc import hpc_list_partitions from .kg_resource import kg_resource from .project import project_list_all from .use_config import set_env from .user import login from .user import logout -hpc_enabled = os.environ.get('PILOT_CLI_HPC_ENABLED', 'false') == 'true' kg_enabled = os.environ.get('PILOT_CLI_KG_ENABLED', 'false') == 'true' def command_groups(): commands = ['file', 'user', 'use_config', 'project', 'dataset', 'container_registry'] - if hpc_enabled: - commands.append('hpc') if kg_enabled: commands.append('kg_resource') return commands @@ -114,20 +104,6 @@ def cr_group(): config_group.add_command(set_env) # Custom commands -if hpc_enabled: - - @entry_point.group(name='hpc') - def hpc_group(): - pass - - hpc_group.add_command(hpc_auth) - hpc_group.add_command(hpc_job_submit) - hpc_group.add_command(hpc_job_info) - hpc_group.add_command(hpc_list_nodes) - hpc_group.add_command(hpc_get_node) - hpc_group.add_command(hpc_list_partitions) - hpc_group.add_command(hpc_get_partition) - if kg_enabled: @entry_point.group(name='kg_resource') diff --git a/app/commands/hpc.py b/app/commands/hpc.py deleted file mode 100644 index d53f4577..00000000 --- a/app/commands/hpc.py +++ /dev/null @@ -1,157 +0,0 @@ -# Copyright (C) 2022-2023 Indoc Research -# -# Contact Indoc Research for any questions regarding the use of this source code. - -import click - -import app.services.logger_services.log_functions as logger -import app.services.output_manager.help_page as hpc_help -from app.configs.user_config import UserConfig -from app.services.hpc_manager.hpc_auth import HPCTokenManager -from app.services.hpc_manager.hpc_cluster import HPCNodeManager -from app.services.hpc_manager.hpc_cluster import HPCPartitionManager -from app.services.hpc_manager.hpc_jobs import HPCJobManager -from app.services.user_authentication.user_login_logout import check_is_active -from app.services.user_authentication.user_login_logout import check_is_login -from app.services.user_authentication.user_login_logout import get_tokens -from app.utils.aggregated import doc - - -@click.command() -def cli(): - """HPC Actions.""" - pass - - -@click.command(name='token') -@click.option('-h', '--host', prompt='Host', help=(hpc_help.hpc_help_page(hpc_help.HpcHELP.HPC_LOGIN_HOST))) -@click.option('-U', '--username', prompt='Username', help=(hpc_help.hpc_help_page(hpc_help.HpcHELP.HPC_LOGIN_USERNAME))) -@click.option( - '-P', - '--password', - prompt='Password', - help=(hpc_help.hpc_help_page(hpc_help.HpcHELP.HPC_LOGIN_PASSWORD)), - hide_input=True, -) -@doc(hpc_help.hpc_help_page(hpc_help.HpcHELP.HPC_AUTH)) -def hpc_auth(host, username, password): - user = UserConfig() - is_login = check_is_login(False) - is_active = check_is_active(False) - # No login session and no input username, password - if not (username and password) and not (is_login and is_active): - username = click.prompt('Username') - password = click.prompt('Password', hide_input=True) - token = get_tokens(username, password)[0] - # Input username and password - elif username and password: - if is_login and is_active: - token = user.access_token - else: - token = get_tokens(username, password)[0] - # No Input username and password, but has login session - elif not (username and password) and (is_login and is_active): - username = user.username - password = user.password - token = user.access_token - hpc_mgr = HPCTokenManager(token) - hpc_token = hpc_mgr.auth_user(host, username, password) - logger.succeed('Authenticated successfully, token saved') - user.hpc_token = hpc_token - user.save() - - -@click.command(name='submit') -@click.option('-h', '--host', prompt='Host', help=(hpc_help.hpc_help_page(hpc_help.HpcHELP.HPC_LOGIN_HOST))) -@click.argument('path', type=click.Path(exists=True), nargs=1) -@doc(hpc_help.hpc_help_page(hpc_help.HpcHELP.HPC_SUBMIT)) -def hpc_job_submit(host, path): - hpc_mgr = HPCJobManager() - submit_job = hpc_mgr.submit_job(host, path) - for k, v in submit_job.items(): - logger.succeed(f'{k}: {v}') - - -@click.command(name='get-job') -@click.option('-h', '--host', prompt='Host', help=(hpc_help.hpc_help_page(hpc_help.HpcHELP.HPC_LOGIN_HOST))) -@click.argument('job_id', type=click.STRING, nargs=1) -@doc(hpc_help.hpc_help_page(hpc_help.HpcHELP.HPC_JOB_INFO)) -def hpc_job_info(host, job_id): - hpc_mgr = HPCJobManager() - job_info = hpc_mgr.get_job(host, job_id) - for k, v in job_info.items(): - if k not in ['standard_input']: - logger.succeed(f'{k}: {v}') - - -@click.command(name='list-nodes') -@click.option('-h', '--host', prompt='Host', help=(hpc_help.hpc_help_page(hpc_help.HpcHELP.HPC_LOGIN_HOST))) -@doc(hpc_help.hpc_help_page(hpc_help.HpcHELP.HPC_NODES)) -def hpc_list_nodes(host): - hpc_mgr = HPCNodeManager() - nodes = hpc_mgr.list_nodes(host) - for node in nodes: - for node_name, node_info in node.items(): - logger.succeed(f'\nNode name: {node_name}') - row_value = '' - for k, v in node_info.items(): - row_value = row_value + k + ': ' + str(v) + ' , ' - logger.info(row_value.rstrip(', ')) - logger.succeed('\nAll nodes are listed') - - -@click.command(name='get-node') -@click.option('-h', '--host', prompt='Host', help=(hpc_help.hpc_help_page(hpc_help.HpcHELP.HPC_LOGIN_HOST))) -@click.argument('node_name', type=click.STRING, nargs=1) -@doc(hpc_help.hpc_help_page(hpc_help.HpcHELP.HPC_GET_NODE)) -def hpc_get_node(host, node_name): - hpc_mgr = HPCNodeManager() - nodes = hpc_mgr.get_node(host, node_name) - for node in nodes: - for node_name, node_info in node.items(): - logger.succeed(f'\nNode name: {node_name}') - row_value = '' - for k, v in node_info.items(): - row_value = row_value + k + ': ' + str(v) + ' , ' - logger.info(row_value.rstrip(', ')) - logger.succeed('\n') - - -@click.command(name='list-partitions') -@click.option('-h', '--host', prompt='Host', help=(hpc_help.hpc_help_page(hpc_help.HpcHELP.HPC_LOGIN_HOST))) -@doc(hpc_help.hpc_help_page(hpc_help.HpcHELP.HPC_PARTITIONS)) -def hpc_list_partitions(host): - hpc_mgr = HPCPartitionManager() - partitions = hpc_mgr.list_partitions(host) - for partition in partitions: - for partition_name, partition_info in partition.items(): - logger.succeed(f'\nPartition name: {partition_name}') - row_value = '' - for k, v in partition_info.items(): - if k == 'tres': - value = str(v).replace(',', ', ') - else: - value = ', '.join(v) - row_value = row_value + k + ': ' + value + ' \n' - logger.info(row_value.rstrip(', ')) - logger.succeed('\nAll partitions are listed') - - -@click.command(name='get-partition') -@click.option('-h', '--host', prompt='Host', help=(hpc_help.hpc_help_page(hpc_help.HpcHELP.HPC_LOGIN_HOST))) -@click.argument('partition_name', type=click.STRING, nargs=1) -@doc(hpc_help.hpc_help_page(hpc_help.HpcHELP.HPC_GET_PARTITION)) -def hpc_get_partition(host, partition_name): - hpc_mgr = HPCPartitionManager() - partitions = hpc_mgr.get_partition(host, partition_name) - for partition in partitions: - for partition_name, partition_info in partition.items(): - logger.succeed(f'\nPartition name: {partition_name}') - row_value = '' - for k, v in partition_info.items(): - if k == 'tres': - value = str(v).replace(',', ', ') - else: - value = ', '.join(v) - row_value = row_value + k + ': ' + value + ' \n' - logger.info(row_value) diff --git a/app/configs/user_config.py b/app/configs/user_config.py index 7b417553..93c7170b 100644 --- a/app/configs/user_config.py +++ b/app/configs/user_config.py @@ -63,7 +63,6 @@ def __init__(self, config_path: Union[str, Path, None] = None, config_filename: 'access_token': '', 'refresh_token': '', 'secret': generate_secret(), - 'hpc_token': '', 'last_active': int(time.time()), 'session_id': '', } @@ -100,7 +99,6 @@ def clear(self): 'api_key': '', 'access_token': '', 'refresh_token': '', - 'hpc_token': '', 'secret': generate_secret(), 'last_active': 0, 'session_id': '', @@ -158,14 +156,6 @@ def secret(self): def secret(self, val): self.config['USER']['secret'] = val - @property - def hpc_token(self): - return decryption(self.config['USER']['hpc_token'], self.secret) - - @hpc_token.setter - def hpc_token(self, val): - self.config['USER']['hpc_token'] = encryption(val, self.secret) - @property def last_active(self): return self.config['USER']['last_active'] diff --git a/app/models/convert_type.py b/app/models/convert_type.py deleted file mode 100644 index 21177330..00000000 --- a/app/models/convert_type.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (C) 2022-2023 Indoc Research -# -# Contact Indoc Research for any questions regarding the use of this source code. - -import ast - -import click - - -class PythonLiteralOption(click.Option): - def type_cast_value(self, ctx, value): - try: - return ast.literal_eval(value) - except Exception: - raise click.BadParameter(value) diff --git a/app/models/service_meta_class.py b/app/models/service_meta_class.py index 534b0f54..8557c3f9 100644 --- a/app/models/service_meta_class.py +++ b/app/models/service_meta_class.py @@ -8,10 +8,3 @@ def __new__(cls, name: str, bases, namespace, **kwargs): if not name.startswith('Srv'): raise TypeError('[Fatal] Invalid Service Statement: class name should start with "Srv"', name) return super().__new__(cls, name, bases, namespace, **kwargs) - - -class HPCMetaService(type): - def __new__(cls, name: str, bases, namespace, **kwargs): - if not name.startswith('HPC'): - raise TypeError('[Fatal] Invalid Service Statement: class name should start with "Srv"', name) - return super().__new__(cls, name, bases, namespace, **kwargs) diff --git a/app/models/upload_form.py b/app/models/upload_form.py index 032d7692..70945d4a 100644 --- a/app/models/upload_form.py +++ b/app/models/upload_form.py @@ -5,98 +5,6 @@ from app.services.file_manager.file_upload.models import FileObject -class FileUploadForm: - def __init__(self): - self._attribute_map = { - 'resumable_identifier': '', - 'resumable_filename': '', - 'resumable_chunk_number': -1, - 'resumable_total_chunks': -1, - 'resumable_total_size': -1, - 'resumable_relative_path': '', - 'tags': [], - 'uploader': '', - 'metadatas': None, - 'container_id': '', - } - - @property - def to_dict(self): - return self._attribute_map - - @property - def resumable_identifier(self): - return self._attribute_map['resumable_identifier'] - - @resumable_identifier.setter - def resumable_identifier(self, resumable_identifier): - self._attribute_map['resumable_identifier'] = resumable_identifier - - @property - def resumable_filename(self): - return self._attribute_map['resumable_filename'] - - @resumable_filename.setter - def resumable_filename(self, resumable_filename): - self._attribute_map['resumable_filename'] = resumable_filename - - @property - def resumable_chunk_number(self): - return self._attribute_map['resumable_chunk_number'] - - @resumable_chunk_number.setter - def resumable_chunk_number(self, resumable_chunk_number): - self._attribute_map['resumable_chunk_number'] = resumable_chunk_number - - @property - def resumable_total_chunks(self): - return self._attribute_map['resumable_total_chunks'] - - @resumable_total_chunks.setter - def resumable_total_chunks(self, resumable_total_chunks): - self._attribute_map['resumable_total_chunks'] = resumable_total_chunks - - @property - def resumable_relative_path(self): - return self._attribute_map['resumable_relative_path'] - - @resumable_relative_path.setter - def resumable_relative_path(self, resumable_relative_path): - self._attribute_map['resumable_relative_path'] = resumable_relative_path.rstrip('/') - - @property - def resumable_total_size(self): - return self._attribute_map['resumable_total_size'] - - @resumable_total_size.setter - def resumable_total_size(self, resumable_total_size): - self._attribute_map['resumable_total_size'] = resumable_total_size - - @property - def tags(self): - return self._attribute_map['tags'] - - @tags.setter - def tags(self, tags): - self._attribute_map['tags'] = tags - - @property - def uploader(self): - return self._attribute_map['uploader'] - - @uploader.setter - def uploader(self, uploader): - self._attribute_map['uploader'] = uploader - - @property - def metadatas(self): - return self._attribute_map['metadatas'] - - @metadatas.setter - def metadatas(self, metadatas): - self._attribute_map['metadatas'] = metadatas - - def generate_on_success_form( project_code: str, operator: str, diff --git a/app/resources/custom_error.py b/app/resources/custom_error.py index bb093d1c..afb6b512 100644 --- a/app/resources/custom_error.py +++ b/app/resources/custom_error.py @@ -94,8 +94,6 @@ class Error: 'DATASET_PERMISSION': 'You do not have permission to access this dataset', 'USER_DISABLED': 'User may not exist or has been disabled', 'OVER_SIZE': '%s is too large', - 'CANNOT_AUTH_HPC': 'Cannot proceed with HPC authorization request', - 'CANNOT_PROCESS_HPC_JOB': 'Cannot process with HPC: %s', 'CONTAINER_REGISTRY_HOST_INVALID': "Invalid host URL. Ensure host begins with 'http://' or 'https://'.", 'CONTAINER_REGISTRY_401': 'You lack valid authentication credentials for the requested resource.', 'CONTAINER_REGISTRY_403': 'You do not have permission to access this host or resource.', diff --git a/app/resources/custom_help.py b/app/resources/custom_help.py index 0da765ba..4c35b293 100644 --- a/app/resources/custom_help.py +++ b/app/resources/custom_help.py @@ -58,19 +58,6 @@ class HelpPage: 'SET_CONFIG': 'Chose config file and set for cli.', 'CONFIG_DESTINATION': 'The destination the config file goes to, default will be current cli directory.', }, - 'hpc': { - 'HPC_AUTH': 'Authorize user to HPC with access token.', - 'HPC_LOGIN_HOST': 'The host address for login HPC.', - 'HPC_LOGIN_USERNAME': 'The username for login HPC.', - 'HPC_LOGIN_PASSWORD': 'The password for login HPC.', - 'HPC_TOKEN': 'The HPC token', - 'HPC_SUBMIT': 'Submit a job to HPC', - 'HPC_JOB_INFO': 'Get a job information', - 'HPC_NODES': 'Get a list of nodes', - 'HPC_GET_NODE': 'Get node information by node name', - 'HPC_PARTITIONS': 'Get a list of partitions', - 'HPC_GET_PARTITION': 'Get partition information by partition name', - }, 'knowledge_graph': { 'KG_IMPORT': 'Import dataset schema into BlueBrainNexus ', 'KG_DATASET_CODE': 'The dataset code', diff --git a/app/services/file_manager/file_upload/upload_client.py b/app/services/file_manager/file_upload/upload_client.py index f4c42b88..f1e7ffb8 100644 --- a/app/services/file_manager/file_upload/upload_client.py +++ b/app/services/file_manager/file_upload/upload_client.py @@ -16,10 +16,10 @@ import httpx -import app.models.upload_form as uf import app.services.output_manager.message_handler as mhandler from app.configs.app_config import AppConfig from app.configs.user_config import UserConfig +from app.models.upload_form import generate_on_success_form from app.services.file_manager.file_upload.models import FileObject from app.services.file_manager.file_upload.models import UploadType from app.services.output_manager.error_handler import ECustomizedError @@ -363,7 +363,7 @@ def on_succeed(self, file_object: FileObject, tags: List[str], chunk_result: Lis for i in range(AppConfig.Env.resilient_retry): url = self.base_url + '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/v1/files' - payload = uf.generate_on_success_form( + payload = generate_on_success_form( self.project_code, self.operator, file_object, diff --git a/app/services/hpc_manager/hpc_auth.py b/app/services/hpc_manager/hpc_auth.py deleted file mode 100644 index dd1300e5..00000000 --- a/app/services/hpc_manager/hpc_auth.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (C) 2022-2023 Indoc Research -# -# Contact Indoc Research for any questions regarding the use of this source code. - -from app.configs.app_config import AppConfig -from app.models.service_meta_class import HPCMetaService -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler -from app.services.user_authentication.decorator import require_valid_token -from app.utils.aggregated import resilient_session - - -class HPCTokenManager(metaclass=HPCMetaService): - def __init__(self, token): - self.token = token - - @require_valid_token() - def auth_user(self, host, username, password): - url = AppConfig.Connections.url_bff + '/v1/hpc/auth' - payload = {'token_issuer': host, 'username': username, 'password': password} - headers = {'Authorization': 'Bearer ' + self.token} - res = resilient_session().post(url, headers=headers, json=payload) - _res = res.json() - code = _res.get('code') - if code == 200: - token = _res.get('result') - return token - else: - SrvErrorHandler.customized_handle(ECustomizedError.CANNOT_AUTH_HPC, True) diff --git a/app/services/hpc_manager/hpc_cluster.py b/app/services/hpc_manager/hpc_cluster.py deleted file mode 100644 index af89d4d0..00000000 --- a/app/services/hpc_manager/hpc_cluster.py +++ /dev/null @@ -1,123 +0,0 @@ -# Copyright (C) 2022-2023 Indoc Research -# -# Contact Indoc Research for any questions regarding the use of this source code. - -from app.configs.app_config import AppConfig -from app.configs.user_config import UserConfig -from app.models.service_meta_class import HPCMetaService -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler -from app.services.user_authentication.decorator import require_valid_token -from app.utils.aggregated import resilient_session - - -class HPCPartitionManager(metaclass=HPCMetaService): - def __init__(self): - self.user = UserConfig() - if self.user.hpc_token: - self.token = self.user.hpc_token - else: - SrvErrorHandler.customized_handle(ECustomizedError.CANNOT_PROCESS_HPC_JOB, True, value='Invalid HPC token') - self.username = self.user.username - - @require_valid_token('kong') - def list_partitions(self, host): - url = AppConfig.Connections.url_bff + '/v1/hpc/partitions' - paramas = {'host': host, 'username': self.username, 'token': self.token} - headers = {'Authorization': 'Bearer ' + self.user.access_token} - res = resilient_session().get(url, headers=headers, params=paramas) - _res = res.json() - code = _res.get('code') - if code == 200: - _info = _res.get('result') - return _info - elif code == 400: - error_msg = _res.get('error_msg') - if 'HPC protocal required' in error_msg: - error_detail = f'missing protocol in the host, try http://{host} or https://{host}' - else: - error_detail = 'Cannot list partitions, please verify your host and try again later' - SrvErrorHandler.customized_handle(ECustomizedError.CANNOT_PROCESS_HPC_JOB, True, value=error_detail) - else: - SrvErrorHandler.customized_handle(ECustomizedError.CANNOT_PROCESS_HPC_JOB, True, alue='List partitions') - - @require_valid_token() - def get_partition(self, host, partition_name): - url = AppConfig.Connections.url_bff + f'/v1/hpc/partitions/{partition_name}' - params = {'host': host, 'username': self.username, 'token': self.token, 'partition_name': partition_name} - headers = {'Authorization': 'Bearer ' + self.user.access_token} - res = resilient_session().get(url, headers=headers, params=params) - _res = res.json() - code = _res.get('code') - if code == 200: - result = _res.get('result') - return result - elif code == 400: - error_msg = _res.get('error_msg') - if 'HPC protocal required' in error_msg: - error_detail = f'missing protocol in the host, try http://{host} or https://{host}' - else: - error_detail = 'Cannot get partition, please verify your partition name and try again later' - SrvErrorHandler.customized_handle(ECustomizedError.CANNOT_PROCESS_HPC_JOB, True, value=error_detail) - elif code == 404: - error_detail = f'Partition {partition_name} may not exist' - SrvErrorHandler.customized_handle(ECustomizedError.CANNOT_PROCESS_HPC_JOB, True, value=error_detail) - else: - error_detail = f'Get partition {partition_name}' - SrvErrorHandler.customized_handle(ECustomizedError.CANNOT_PROCESS_HPC_JOB, True, value=error_detail) - - -class HPCNodeManager(metaclass=HPCMetaService): - def __init__(self): - self.user = UserConfig() - if self.user.hpc_token: - self.token = self.user.hpc_token - else: - SrvErrorHandler.customized_handle(ECustomizedError.CANNOT_PROCESS_HPC_JOB, True, value='Invalid HPC token') - self.username = self.user.username - - @require_valid_token() - def get_node(self, host, node_name): - url = AppConfig.Connections.url_bff + f'/v1/hpc/nodes/{node_name}' - params = {'host': host, 'username': self.username, 'token': self.token, 'node_name': node_name} - headers = {'Authorization': 'Bearer ' + self.user.access_token} - res = resilient_session().get(url, headers=headers, params=params) - _res = res.json() - code = _res.get('code') - if code == 200: - result = _res.get('result') - return result - elif code == 400: - error_msg = _res.get('error_msg') - if 'HPC protocal required' in error_msg: - error_detail = f'missing protocol in the host, try http://{host} or https://{host}' - else: - error_detail = 'Cannot get node information, please verify your node name and try again later' - SrvErrorHandler.customized_handle(ECustomizedError.CANNOT_PROCESS_HPC_JOB, True, value=error_detail) - elif code == 404: - error_detail = f'Node {node_name} may not exist' - SrvErrorHandler.customized_handle(ECustomizedError.CANNOT_PROCESS_HPC_JOB, True, value=error_detail) - else: - error_detail = f'Get node {node_name}' - SrvErrorHandler.customized_handle(ECustomizedError.CANNOT_PROCESS_HPC_JOB, True, value=error_detail) - - @require_valid_token() - def list_nodes(self, host): - url = AppConfig.Connections.url_bff + '/v1/hpc/nodes' - paramas = {'host': host, 'username': self.username, 'token': self.token} - headers = {'Authorization': 'Bearer ' + self.user.access_token} - res = resilient_session().get(url, headers=headers, params=paramas) - _res = res.json() - code = _res.get('code') - if code == 200: - _info = _res.get('result') - return _info - elif code == 400: - error_msg = _res.get('error_msg') - if 'HPC protocal required' in error_msg: - error_detail = f'missing protocol in the host, try http://{host} or https://{host}' - else: - error_detail = 'Cannot list nodes, please verify your host and try again later' - SrvErrorHandler.customized_handle(ECustomizedError.CANNOT_PROCESS_HPC_JOB, True, value=error_detail) - else: - SrvErrorHandler.customized_handle(ECustomizedError.CANNOT_PROCESS_HPC_JOB, True, value='List nodes') diff --git a/app/services/hpc_manager/hpc_jobs.py b/app/services/hpc_manager/hpc_jobs.py deleted file mode 100644 index ed89f511..00000000 --- a/app/services/hpc_manager/hpc_jobs.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (C) 2022-2023 Indoc Research -# -# Contact Indoc Research for any questions regarding the use of this source code. - -import json - -from app.configs.app_config import AppConfig -from app.configs.user_config import UserConfig -from app.models.service_meta_class import HPCMetaService -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler -from app.services.output_manager.response_handler import HPCJobInfoResponse -from app.services.output_manager.response_handler import HPCJobSubmitResponse -from app.services.user_authentication.decorator import require_valid_token -from app.utils.aggregated import resilient_session - - -class HPCJobManager(metaclass=HPCMetaService): - def __init__(self): - self.user = UserConfig() - self.token = ( - self.user.hpc_token - if self.user.hpc_token - else SrvErrorHandler.customized_handle( - ECustomizedError.CANNOT_PROCESS_HPC_JOB, True, value='Invalid HPC token' - ) - ) - self.username = self.user.username - - def pre_load_data(self, path): - json_data = {} - try: - with open(path) as f: - json_data = json.load(f) - f.close() - except json.decoder.JSONDecodeError: - SrvErrorHandler.customized_handle(ECustomizedError.INVALID_ACTION, False, f'{path} is an invalid json file') - except Exception: - SrvErrorHandler.customized_handle(ECustomizedError.INVALID_ACTION, False, f'{path} is an invalid json file') - return json_data - - @require_valid_token() - def submit_job(self, host, path): - url = AppConfig.Connections.url_bff + '/v1/hpc/job' - job_info = self.pre_load_data(path) - payload = {'host': host, 'username': self.username, 'token': self.token, 'job_info': job_info} - headers = {'Authorization': 'Bearer ' + self.user.access_token} - res = resilient_session().post(url, headers=headers, json=payload) - _res = res.json() - code = _res.get('code') - result = _res.get('result') - _ = getattr(HPCJobSubmitResponse(payload, _res), f'return_{code}_response')() - return result - - @require_valid_token() - def get_job(self, host, job_id): - url = AppConfig.Connections.url_bff + f'/v1/hpc/job/{job_id}' - params = {'host': host, 'username': self.username, 'token': self.token} - headers = {'Authorization': 'Bearer ' + self.user.access_token} - response = resilient_session().get(url, headers=headers, params=params) - _res = response.json() - code = _res.get('code') - result = _res.get('result') - params['job_id'] = job_id - _ = getattr(HPCJobInfoResponse(params, _res), f'return_{code}_response')() - return result diff --git a/app/services/output_manager/error_handler.py b/app/services/output_manager/error_handler.py index bf1d02ae..3d71cf9c 100644 --- a/app/services/output_manager/error_handler.py +++ b/app/services/output_manager/error_handler.py @@ -67,8 +67,6 @@ class ECustomizedError(enum.Enum): DATASET_NOT_EXIST = 'DATASET_NOT_EXIST' DATASET_PERMISSION = 'DATASET_PERMISSION' USER_DISABLED = 'USER_DISABLED' - CANNOT_AUTH_HPC = 'CANNOT_AUTH_HPC' - CANNOT_PROCESS_HPC_JOB = 'CANNOT_PROCESS_HPC_JOB' OVER_SIZE = 'OVER_SIZE' CONTAINER_REGISTRY_HOST_INVALID = 'CONTAINER_REGISTRY_HOST_INVALID' CONTAINER_REGISTRY_401 = 'CONTAINER_REGISTRY_401' diff --git a/app/services/output_manager/help_page.py b/app/services/output_manager/help_page.py index 68102f70..a75789da 100644 --- a/app/services/output_manager/help_page.py +++ b/app/services/output_manager/help_page.py @@ -89,25 +89,6 @@ def file_help_page(FileHELP: FileHELP): return helps.get(FileHELP.name) -class HpcHELP(enum.Enum): - HPC_AUTH = 'HPC_AUTH' - HPC_LOGIN_HOST = 'HPC_LOGIN_HOST' - HPC_LOGIN_USERNAME = 'HPC_LOGIN_USERNAME' - HPC_LOGIN_PASSWORD = 'HPC_LOGIN_PASSWORD' - HPC_TOKEN = 'HPC_TOKEN' - HPC_SUBMIT = 'HPC_SUBMIT' - HPC_JOB_INFO = 'HPC_JOB_INFO' - HPC_NODES = 'HPC_NODES' - HPC_GET_NODE = 'HPC_GET_NODE' - HPC_PARTITIONS = 'HPC_PARTITIONS' - HPC_GET_PARTITION = 'HPC_GET_PARTITION' - - -def hpc_help_page(HpcHELP: HpcHELP): - helps = help_msg.get('hpc', 'default hpc help') - return helps.get(HpcHELP.name) - - class KgResourceHELP(enum.Enum): KG_IMPORT = 'KG_IMPORT' KG_DATASET_CODE = 'KG_DATASET_CODE' diff --git a/app/services/output_manager/response_handler.py b/app/services/output_manager/response_handler.py deleted file mode 100644 index 8d394f31..00000000 --- a/app/services/output_manager/response_handler.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (C) 2022-2023 Indoc Research -# -# Contact Indoc Research for any questions regarding the use of this source code. - -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler - - -class HPCJobInfoResponse: - def __init__(self, payload: dict, response: dict): - self.payload = payload - self.res = response - - def return_200_response(self): - pass - - def return_400_response(self): - error_msg = self.res.get('error_msg') - host = self.payload.get('host') - if 'HPC protocal required' in error_msg: - error_detail = f'missing protocol in the host, try http://{host} or https://{host}' - else: - error_detail = 'Cannot get job, please verify your job ID and try again later' - SrvErrorHandler.customized_handle(ECustomizedError.CANNOT_PROCESS_HPC_JOB, True, value=error_detail) - - def return_404_response(self): - error_msg = self.res.get('error_msg') - job_id = self.payload.get('job_id') - host = self.payload.get('host') - if 'Job ID' in error_msg: - error_detail = f'job {job_id} may not exist' - elif 'Host not found' in error_msg: - error_detail = f'host {host} may not exist' - SrvErrorHandler.customized_handle(ECustomizedError.CANNOT_PROCESS_HPC_JOB, True, value=error_detail) - - def return_500_response(self): - job_id = self.payload.get('job_id') - error_detail = f'Job ID {job_id}' - SrvErrorHandler.customized_handle(ECustomizedError.CANNOT_PROCESS_HPC_JOB, True, value=error_detail) - - -class HPCJobSubmitResponse: - def __init__(self, payload: dict, response: dict) -> None: - self.payload = payload - self.res = response - - def return_200_response(self): - pass - - def return_400_response(self): - error_msg = self.res.get('error_msg') - host = self.payload.get('host') - if 'Missing script' in error_msg: - error_detail = f'{error_msg} in the job json file' - elif 'HPC protocal required' in error_msg: - error_detail = f'missing protocol in the host, try http://{host} or https://{host}' - else: - error_detail = 'Cannot submit job, please verify your json file and try again later' - SrvErrorHandler.customized_handle(ECustomizedError.CANNOT_PROCESS_HPC_JOB, True, value=error_detail) - - def return_403_response(self): - error_detail = self.res.get('error_msg') - SrvErrorHandler.customized_handle(ECustomizedError.CANNOT_PROCESS_HPC_JOB, True, value=error_detail) - - def return_500_response(self): - path = self.payload.get('path') - error_detail = f'submit job {path}' - SrvErrorHandler.customized_handle(ECustomizedError.CANNOT_PROCESS_HPC_JOB, True, value=error_detail) - - -class HPCListPartitionsResponse: - def __init__(self) -> None: - pass diff --git a/app/services/user_authentication/user_login_logout.py b/app/services/user_authentication/user_login_logout.py index b6e8882f..0cad5c5f 100644 --- a/app/services/user_authentication/user_login_logout.py +++ b/app/services/user_authentication/user_login_logout.py @@ -48,7 +48,6 @@ def login_using_api_key(api_key: str) -> bool: user_config.refresh_token = '' user_config.username = username user_config.last_active = str(int(time.time())) - user_config.hpc_token = '' user_config.session_id = 'cli-' + str(uuid4()) user_config.save() @@ -107,7 +106,6 @@ def validate_user_device_login(device_code: str, expires: int, interval: int) -> user_config.refresh_token = resp_dict['refresh_token'] user_config.username = decode_token['preferred_username'] user_config.last_active = str(int(time.time())) - user_config.hpc_token = '' user_config.session_id = 'cli-' + str(uuid4()) user_config.save() diff --git a/app/utils/aggregated.py b/app/utils/aggregated.py index 281eee63..a5c48e53 100644 --- a/app/utils/aggregated.py +++ b/app/utils/aggregated.py @@ -2,7 +2,6 @@ # # Contact Indoc Research for any questions regarding the use of this source code. -import datetime import os import re import shutil @@ -18,10 +17,6 @@ from env import ConfigClass -def get_current_datetime(): - return datetime.datetime.now().isoformat() - - def resilient_session(): # each resilient session will headers = {'VM-Info': ConfigClass.VM_INFO} diff --git a/tests/app/services/hpc_manager/test_hpc_auth.py b/tests/app/services/hpc_manager/test_hpc_auth.py deleted file mode 100644 index e31dae13..00000000 --- a/tests/app/services/hpc_manager/test_hpc_auth.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (C) 2022-2023 Indoc Research -# -# Contact Indoc Research for any questions regarding the use of this source code. - -import pytest - -from app.services.hpc_manager.hpc_auth import HPCTokenManager - - -def test_hpc_auth(httpx_mock, mocker): - mocker.patch('app.services.user_authentication.token_manager.SrvTokenManager.check_valid', return_value=0) - httpx_mock.add_response( - method='POST', - url='http://bff_cli' + '/v1/hpc/auth', - json={'code': 200, 'error_msg': '', 'result': 'fake-token'}, - status_code=200, - ) - hpc_mgr = HPCTokenManager('fake_token') - token = hpc_mgr.auth_user('test_host', 'test_user', 'test_password') - assert token == 'fake-token' - - -def test_hpc_auth_failed(httpx_mock, mocker, capsys): - mocker.patch('app.services.user_authentication.token_manager.SrvTokenManager.check_valid', return_value=0) - httpx_mock.add_response( - method='POST', - url='http://bff_cli' + '/v1/hpc/auth', - json={'code': 500, 'error_msg': 'User authorization failed: Authentication failed.', 'result': []}, - ) - hpc_mgr = HPCTokenManager('fake_token') - with pytest.raises(SystemExit): - hpc_mgr.auth_user('test_host', 'test_user', 'test_password') - out, err = capsys.readouterr() - assert out == 'Cannot proceed with HPC authorization request\n' diff --git a/tests/app/services/hpc_manager/test_hpc_cluster.py b/tests/app/services/hpc_manager/test_hpc_cluster.py deleted file mode 100644 index 986c7efc..00000000 --- a/tests/app/services/hpc_manager/test_hpc_cluster.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (C) 2022-2023 Indoc Research -# -# Contact Indoc Research for any questions regarding the use of this source code. - -import pytest - -from app.configs.user_config import UserConfig -from app.resources.custom_error import Error -from app.services.hpc_manager.hpc_cluster import HPCPartitionManager -from app.services.output_manager.error_handler import ECustomizedError -from tests.conftest import decoded_token - - -def test_hpc_list_partitions(httpx_mock, mocker): - mocker.patch( - 'app.services.user_authentication.token_manager.SrvTokenManager.decode_access_token', - return_value=decoded_token(), - ) - user_config = UserConfig() - user_config.username = 'test-user' - user_config.hpc_token = 'test-hpc-token' - httpx_mock.add_response( - method='GET', - url='http://bff_cli/v1/hpc/partitions?host=test_host&username=test-user&token=test-hpc-token', - json={ - 'code': 200, - 'error_msg': '', - 'result': [ - {'partition1': {'nodes': ['hpc-node1'], 'tres': 'cpu=2,mem=4G,node=1,billing=2'}}, - {'partition2': {'nodes': ['hpc-node2'], 'tres': 'cpu=1,mem=8G,node=1,billing=1'}}, - ], - }, - ) - - expected_partitions = [ - {'partition1': {'nodes': ['hpc-node1'], 'tres': 'cpu=2,mem=4G,node=1,billing=2'}}, - {'partition2': {'nodes': ['hpc-node2'], 'tres': 'cpu=1,mem=8G,node=1,billing=1'}}, - ] - hpc_mgr = HPCPartitionManager() - partion = hpc_mgr.list_partitions('test_host') - assert partion == expected_partitions - - -def test_hpc_list_partitions_no_token(mocker, capsys): - mocker.patch( - 'app.services.user_authentication.token_manager.SrvTokenManager.decode_access_token', - return_value=decoded_token(), - ) - user_config = UserConfig() - user_config.hpc_token = '' - with pytest.raises(SystemExit): - hpc_mgr = HPCPartitionManager() - _ = hpc_mgr.list_partitions('test_host') - out, err = capsys.readouterr() - assert out == Error.error_msg.get(ECustomizedError.CANNOT_PROCESS_HPC_JOB.name) % 'Invalid HPC token\n' diff --git a/tests/conftest.py b/tests/conftest.py index ea0c6b85..ec2653c9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -31,7 +31,6 @@ def mock_settings(monkeypatch, mocker): monkeypatch.setattr(UserConfig, 'api_key', 'test-api-key') monkeypatch.setattr(UserConfig, 'access_token', 'test-access-token') monkeypatch.setattr(UserConfig, 'refresh_token', 'test-refresh-token') - monkeypatch.setattr(UserConfig, 'hpc_token', 'test-hpc-token') mocker.patch('app.configs.user_config.UserConfig.save') # Do not save config when running tests From 70301abb6299fbbbe807e831628ba30e59006a67 Mon Sep 17 00:00:00 2001 From: Color Zhan Date: Thu, 13 Jul 2023 10:19:36 -0400 Subject: [PATCH 06/12] PILOT-3280: add the error handler to search_item function (#73) * add the error handler to search_item function * update the error handling by using response.text --------- Co-authored-by: zhiren --- app/utils/aggregated.py | 2 ++ tests/app/utils/test_aggregated.py | 32 ++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/app/utils/aggregated.py b/app/utils/aggregated.py index a5c48e53..099339af 100644 --- a/app/utils/aggregated.py +++ b/app/utils/aggregated.py @@ -39,6 +39,8 @@ def search_item(project_code, zone, folder_relative_path, item_type, container_t res = requests.get(url, params=params, headers=headers) if res.status_code == 403: SrvErrorHandler.customized_handle(ECustomizedError.PERMISSION_DENIED, project_code) + elif res.status_code != 200: + SrvErrorHandler.default_handle(res.text, True) return res.json() diff --git a/tests/app/utils/test_aggregated.py b/tests/app/utils/test_aggregated.py index a6c48d5e..ba736970 100644 --- a/tests/app/utils/test_aggregated.py +++ b/tests/app/utils/test_aggregated.py @@ -2,6 +2,8 @@ # # Contact Indoc Research for any questions regarding the use of this source code. +import pytest + from app.utils.aggregated import search_item test_project_code = 'testproject' @@ -55,3 +57,33 @@ def test_search_file_should_return_200(requests_mock, mocker): } res = search_item(test_project_code, 'zone', 'folder_relative_path', 'file', 'project') assert res['result'] == expected_result + + +def test_search_file_error_handling_with_403(requests_mock, mocker, capsys): + mocker.patch('app.services.user_authentication.token_manager.SrvTokenManager.check_valid', return_value=0) + requests_mock.get( + f'http://bff_cli/v1/project/{test_project_code}/search', + json={}, + status_code=403, + ) + with pytest.raises(SystemExit): + search_item(test_project_code, 'zone', 'folder_relative_path', 'file', 'project') + out, _ = capsys.readouterr() + assert ( + out.rstrip() + == 'Permission denied. Please verify your role in the Project has permission to perform this action.' + ) + + +def test_search_file_error_handling_with_401(requests_mock, mocker, capsys): + mocker.patch('app.services.user_authentication.token_manager.SrvTokenManager.check_valid', return_value=0) + requests_mock.get( + f'http://bff_cli/v1/project/{test_project_code}/search', + text='Authentication failed.', + status_code=401, + ) + with pytest.raises(SystemExit): + search_item(test_project_code, 'zone', 'folder_relative_path', 'file', 'project') + out, _ = capsys.readouterr() + out = out.rstrip() + assert out == 'Authentication failed.' From c0d34e1c108079a93c1a9eee9822e87f1b329191 Mon Sep 17 00:00:00 2001 From: Vadym Moshynskyi Date: Fri, 14 Jul 2023 11:21:53 +0200 Subject: [PATCH 07/12] PILOT-3305: Remove kg_resource command functionality (#75) * PILOT-3305: Remove kg_resource command functionality * PILOT-3305: Make container_registry disabled by default --- app/commands/entry_point.py | 33 ++-- app/commands/kg_resource.py | 23 --- app/resources/custom_help.py | 4 - app/services/kg_manager/kg_resource.py | 89 --------- app/services/output_manager/error_handler.py | 5 - app/services/output_manager/help_page.py | 12 +- .../services/kg_manager/test_kg_manager.py | 172 ------------------ 7 files changed, 14 insertions(+), 324 deletions(-) delete mode 100644 app/commands/kg_resource.py delete mode 100644 app/services/kg_manager/kg_resource.py delete mode 100644 tests/app/services/kg_manager/test_kg_manager.py diff --git a/app/commands/entry_point.py b/app/commands/entry_point.py index a16da236..e9ed194c 100644 --- a/app/commands/entry_point.py +++ b/app/commands/entry_point.py @@ -25,19 +25,18 @@ from .file import file_resume # Import custom commands -from .kg_resource import kg_resource from .project import project_list_all from .use_config import set_env from .user import login from .user import logout -kg_enabled = os.environ.get('PILOT_CLI_KG_ENABLED', 'false') == 'true' +container_registry_enabled = os.environ.get('PILOT_CLI_CONTAINER_REGISTRY_ENABLED', 'false') == 'true' def command_groups(): - commands = ['file', 'user', 'use_config', 'project', 'dataset', 'container_registry'] - if kg_enabled: - commands.append('kg_resource') + commands = ['file', 'user', 'use_config', 'project', 'dataset'] + if container_registry_enabled: + commands.append('container_registry') return commands @@ -78,12 +77,6 @@ def config_group(): pass -@entry_point.group(name='container_registry') -@require_config -def cr_group(): - pass - - file_group.add_command(file_put) file_group.add_command(file_check_manifest) file_group.add_command(file_export_manifest) @@ -96,18 +89,18 @@ def cr_group(): dataset_group.add_command(dataset_list) dataset_group.add_command(dataset_show_detail) dataset_group.add_command(dataset_download) -cr_group.add_command(list_projects) -cr_group.add_command(list_repositories) -cr_group.add_command(create_project) -cr_group.add_command(get_secret) -cr_group.add_command(invite_member) config_group.add_command(set_env) # Custom commands -if kg_enabled: +if container_registry_enabled: - @entry_point.group(name='kg_resource') - def kg_resource_group(): + @entry_point.group(name='container_registry') + @require_config + def cr_group(): pass - kg_resource_group.add_command(kg_resource) + cr_group.add_command(list_projects) + cr_group.add_command(list_repositories) + cr_group.add_command(create_project) + cr_group.add_command(get_secret) + cr_group.add_command(invite_member) diff --git a/app/commands/kg_resource.py b/app/commands/kg_resource.py deleted file mode 100644 index 5ddb43f4..00000000 --- a/app/commands/kg_resource.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (C) 2022-2023 Indoc Research -# -# Contact Indoc Research for any questions regarding the use of this source code. - -import click - -import app.services.output_manager.help_page as kg_help -from app.services.kg_manager.kg_resource import SrvKGResourceMgr -from app.utils.aggregated import doc - - -@click.command() -def cli(): - """KnowledgeGraph Actions.""" - pass - - -@click.command(name='import') -@click.argument('paths', type=click.Path(exists=True), nargs=-1) -@doc(kg_help.kg_resource_help_page(kg_help.KgResourceHELP.KG_IMPORT)) -def kg_resource(paths): - kg = SrvKGResourceMgr(paths) - kg.import_resource() diff --git a/app/resources/custom_help.py b/app/resources/custom_help.py index 4c35b293..6c154ae5 100644 --- a/app/resources/custom_help.py +++ b/app/resources/custom_help.py @@ -58,10 +58,6 @@ class HelpPage: 'SET_CONFIG': 'Chose config file and set for cli.', 'CONFIG_DESTINATION': 'The destination the config file goes to, default will be current cli directory.', }, - 'knowledge_graph': { - 'KG_IMPORT': 'Import dataset schema into BlueBrainNexus ', - 'KG_DATASET_CODE': 'The dataset code', - }, 'container_registry': { 'LIST_PROJECTS': 'List all projects', 'LIST_REPOSITORIES': 'List all repositories (optionally: in a given project)', diff --git a/app/services/kg_manager/kg_resource.py b/app/services/kg_manager/kg_resource.py deleted file mode 100644 index b2adc0f4..00000000 --- a/app/services/kg_manager/kg_resource.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (C) 2022-2023 Indoc Research -# -# Contact Indoc Research for any questions regarding the use of this source code. - -import collections -import json -import os - -import app.services.logger_services.log_functions as logger -from app.configs.app_config import AppConfig -from app.configs.user_config import UserConfig -from app.models.service_meta_class import MetaService -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import OverSizeError -from app.services.output_manager.error_handler import SrvErrorHandler -from app.services.output_manager.error_handler import customized_error_msg -from app.services.user_authentication.decorator import require_valid_token -from app.utils.aggregated import get_file_in_folder -from app.utils.aggregated import resilient_session - - -class SrvKGResourceMgr(metaclass=MetaService): - def __init__(self, paths): - self.user = UserConfig() - self.paths = paths - - def pre_load_data(self, paths): - json_data = {} - for path in paths: - invalid_json_msg = f'{path} is an invalid json file' - try: - self.validate_file_size(path) - with open(path) as f: - json_data[path] = json.load(f) - except json.decoder.JSONDecodeError: - SrvErrorHandler.customized_handle(ECustomizedError.INVALID_ACTION, False, invalid_json_msg) - continue - except OverSizeError as e: - SrvErrorHandler.default_handle(str(e), False) - continue - except Exception: - SrvErrorHandler.customized_handle(ECustomizedError.INVALID_ACTION, False, invalid_json_msg) - continue - return json_data - - def validate_file_size(self, path): - size = os.path.getsize(path) - if size > 1000000: - raise OverSizeError(customized_error_msg(ECustomizedError.OVER_SIZE) % path) - - @require_valid_token() - def import_resource(self): - url = AppConfig.Connections.url_bff + '/v1/kg/resources' - file_to_process = [] - try: - for path in self.paths: - path = os.path.relpath(path) - if os.path.isdir(path): - files = get_file_in_folder(path) - file_to_process = file_to_process + files - else: - file_to_process.append(path) - duplicate_file_list = [f for f, count in collections.Counter(file_to_process).items() if count > 1] - if duplicate_file_list: - duplicate_files = ', \n'.join(duplicate_file_list) - logger.warning(f'Following files have multiple input, it will process one time: \n{duplicate_files}') - json_data = self.pre_load_data(file_to_process) - if not json_data: - return - - payload = {'dataset_code': [], 'data': json_data} - headers = {'Authorization': 'Bearer ' + self.user.access_token} - res = resilient_session().post(url, headers=headers, json=payload) - response = res.json() - code = response.get('code') - result = response.get('result') - if code == 200: - ignored = result.get('ignored') - processed = result.get('processing') - if ignored: - ignored_files = ', \n'.join(list(ignored.keys())) - logger.warning(f'File skipped: \n{ignored_files}') - if processed: - processed_files = ', \n'.join(list(processed.keys())) - logger.succeed(f'File imported: \n{processed_files}') - else: - SrvErrorHandler.customized_handle(ECustomizedError.ERROR_CONNECTION, True) - except Exception as e: - SrvErrorHandler.customized_handle(ECustomizedError.INVALID_ACTION, False, str(e)) diff --git a/app/services/output_manager/error_handler.py b/app/services/output_manager/error_handler.py index 3d71cf9c..452b812e 100644 --- a/app/services/output_manager/error_handler.py +++ b/app/services/output_manager/error_handler.py @@ -110,8 +110,3 @@ def customized_handle(customized_error: ECustomizedError, if_exit=False, value=N logger.error(customized_error_msg(customized_error)) if if_exit: sys.exit(0) - - -class OverSizeError(Exception): - def __init__(self, message='File size is too large'): - super().__init__(message) diff --git a/app/services/output_manager/help_page.py b/app/services/output_manager/help_page.py index a75789da..033c16ce 100644 --- a/app/services/output_manager/help_page.py +++ b/app/services/output_manager/help_page.py @@ -89,16 +89,6 @@ def file_help_page(FileHELP: FileHELP): return helps.get(FileHELP.name) -class KgResourceHELP(enum.Enum): - KG_IMPORT = 'KG_IMPORT' - KG_DATASET_CODE = 'KG_DATASET_CODE' - - -def kg_resource_help_page(KgResourceHELP: KgResourceHELP): - helps = help_msg.get('knowledge_graph', 'default kg help') - return helps.get(KgResourceHELP.name) - - class ContainerRegistryHELP(enum.Enum): LIST_PROJECTS = 'LIST_PROJECTS' LIST_REPOSITORIES = 'LIST_REPOSITORIES' @@ -108,5 +98,5 @@ class ContainerRegistryHELP(enum.Enum): def cr_help_page(ContainerRegistryHELP: ContainerRegistryHELP): - helps = help_msg.get('container_registry', 'default kg help') + helps = help_msg.get('container_registry', 'default container_registry help') return helps.get(ContainerRegistryHELP.name) diff --git a/tests/app/services/kg_manager/test_kg_manager.py b/tests/app/services/kg_manager/test_kg_manager.py deleted file mode 100644 index cf422069..00000000 --- a/tests/app/services/kg_manager/test_kg_manager.py +++ /dev/null @@ -1,172 +0,0 @@ -# Copyright (C) 2022-2023 Indoc Research -# -# Contact Indoc Research for any questions regarding the use of this source code. - -from app.services.kg_manager.kg_resource import SrvKGResourceMgr - - -def test_import_kg(httpx_mock, mocker, capsys): - mocker.patch('app.services.user_authentication.token_manager.SrvTokenManager.check_valid', return_value=0) - mocker.patch('os.path.isdir', return_value=False) - mocker.patch( - 'app.services.kg_manager.kg_resource.SrvKGResourceMgr.pre_load_data', - return_value={'json1': 'data1', 'json2': 'data2'}, - ) - httpx_mock.add_response( - method='POST', - url='http://bff_cli/v1/kg/resources', - json={ - 'code': 200, - 'error_msg': '', - 'result': { - 'processing': { - 'sample_file.json': { - '@context': 'https://bluebrain.github.io/nexus/contexts/metadata.json', - '@id': 'http://test_domain/kg/v1/resources/dataset/_/d09d9594-e495-425b-a6a6-68e230ee139e', - '@type': 'http://test_domain/kg/v1/vocabs/dataset/Not_Specified', - '_constrainedBy': 'https://bluebrain.github.io/nexus/schemas/unconstrained.json', - '_createdAt': '2022-03-23T13:55:51.897Z', - '_createdBy': 'http://test_domain/kg/v1/realms/users/test_user', - '_deprecated': False, - '_incoming': ( - 'http://test_domain/kg/v1/resources/dataset/_/' - 'd09d9594-e495-425b-a6a6-68e230ee139e/incoming' - ), - '_outgoing': ( - 'http://test_domain/kg/v1/resources/dataset/_/' - 'd09d9594-e495-425b-a6a6-68e230ee139e/outgoing' - ), - '_project': 'http://test_domain/kg/v1/projects/dataset', - '_rev': 1, - '_schemaProject': 'http://test_domain/kg/v1/projects/dataset', - '_self': 'http://test_domain/kg/v1/resources/dataset/_/d09d9594-e495-425b-a6a6-68e230ee139e', - '_updatedAt': '2022-03-23T13:55:51.897Z', - '_updatedBy': 'http://test_domain/kg/v1/realms/users/test_user', - } - }, - 'ignored': {}, - }, - }, - ) - - kg_mgr = SrvKGResourceMgr(('fake_file.json',)) - kg_mgr.import_resource() - out, _ = capsys.readouterr() - assert out == 'File imported: \nsample_file.json\n' - - -def test_import_kg_invalid_json(mocker, capsys): - mocker.patch('app.services.user_authentication.token_manager.SrvTokenManager.check_valid', return_value=0) - mocker.patch('os.path.isdir', return_value=False) - kg_mgr = SrvKGResourceMgr(('fake_file.csv',)) - kg_mgr.import_resource() - out, err = capsys.readouterr() - assert out == 'Invalid action: fake_file.csv is an invalid json file\n' - - -def test_import_kg_folder(httpx_mock, mocker, capsys): - mocker.patch('app.services.user_authentication.token_manager.SrvTokenManager.check_valid', return_value=0) - mocker.patch('os.path.isdir', return_value=True) - mocker.patch( - 'app.services.kg_manager.kg_resource.SrvKGResourceMgr.pre_load_data', - return_value={'json1': 'data1', 'json2': 'data2'}, - ) - httpx_mock.add_response( - method='POST', - url='http://bff_cli/v1/kg/resources', - json={ - 'code': 200, - 'error_msg': '', - 'result': { - 'processing': { - 'json_test/fake_file1.json': { - '@context': 'https://bluebrain.github.io/nexus/contexts/metadata.json', - '@id': 'http://test_domain/kg/v1/resources/dataset/_/6b3d6b08-921a-4e73-9f53-93bdb77d76e1', - '@type': 'http://test_domain/kg/v1/vocabs/dataset/Not_Specified', - '_constrainedBy': 'https://bluebrain.github.io/nexus/schemas/unconstrained.json', - '_createdAt': '2022-03-23T18:40:23.698Z', - '_createdBy': 'http://test_domain/kg/v1/realms/users/test_user', - '_deprecated': False, - '_incoming': ( - 'http://test_domain/kg/v1/resources/dataset/_/' - '6b3d6b08-921a-4e73-9f53-93bdb77d76e1/incoming' - ), - '_outgoing': ( - 'http://test_domain/kg/v1/resources/dataset/_/' - '6b3d6b08-921a-4e73-9f53-93bdb77d76e1/outgoing' - ), - '_project': 'http://test_domain/kg/v1/projects/dataset', - '_rev': 1, - '_schemaProject': 'http://test_domain/kg/v1/projects/dataset', - '_self': 'http://test_domain/kg/v1/resources/dataset/_/6b3d6b08-921a-4e73-9f53-93bdb77d76e1', - '_updatedAt': '2022-03-23T18:40:23.698Z', - '_updatedBy': 'http://test_domain/kg/v1/realms/users/test_user', - }, - 'json_test/fake_file2.json': { - '@context': 'https://bluebrain.github.io/nexus/contexts/metadata.json', - '@id': 'http://test_domain/kg/v1/resources/dataset/_/b3088a27-4a44-4bf0-a952-942f9bf8a4f4', - '@type': 'http://test_domain/kg/v1/vocabs/dataset/Not_Specified', - '_constrainedBy': 'https://bluebrain.github.io/nexus/schemas/unconstrained.json', - '_createdAt': '2022-03-23T18:40:23.882Z', - '_createdBy': 'http://test_domain/kg/v1/realms/users/test_user', - '_deprecated': False, - '_incoming': ( - 'http://test_domain/kg/v1/resources/dataset/_/' - 'b3088a27-4a44-4bf0-a952-942f9bf8a4f4/incoming' - ), - '_outgoing': ( - 'http://test_domain/kg/v1/resources/dataset/_/' - 'b3088a27-4a44-4bf0-a952-942f9bf8a4f4/outgoing' - ), - '_project': 'http://test_domain/kg/v1/projects/dataset', - '_rev': 1, - '_schemaProject': 'http://test_domain/kg/v1/projects/dataset', - '_self': 'http://test_domain/kg/v1/resources/dataset/_/b3088a27-4a44-4bf0-a952-942f9bf8a4f4', - '_updatedAt': '2022-03-23T18:40:23.882Z', - '_updatedBy': 'http://test_domain/kg/v1/realms/users/test_user', - }, - 'json_test/fake_file3.json': { - '@context': 'https://bluebrain.github.io/nexus/contexts/metadata.json', - '@id': 'http://test_domain/kg/v1/resources/dataset/_/c45b0998-65e0-4436-a6fe-0a5b4fb1c854', - '@type': 'http://test_domain/kg/v1/vocabs/dataset/Not_Specified', - '_constrainedBy': 'https://bluebrain.github.io/nexus/schemas/unconstrained.json', - '_createdAt': '2022-03-23T18:40:24.071Z', - '_createdBy': 'http://test_domain/kg/v1/realms/users/test_user', - '_deprecated': False, - '_incoming': ( - 'http://test_domain/kg/v1/resources/dataset/_/' - 'c45b0998-65e0-4436-a6fe-0a5b4fb1c854/incoming' - ), - '_outgoing': ( - 'http://test_domain/kg/v1/resources/dataset/_/' - 'c45b0998-65e0-4436-a6fe-0a5b4fb1c854/outgoing' - ), - '_project': 'http://test_domain/kg/v1/projects/dataset', - '_rev': 1, - '_schemaProject': 'http://test_domain/kg/v1/projects/dataset', - '_self': 'http://test_domain/kg/v1/resources/dataset/_/c45b0998-65e0-4436-a6fe-0a5b4fb1c854', - '_updatedAt': '2022-03-23T18:40:24.071Z', - '_updatedBy': 'http://test_domain/kg/v1/realms/users/test_user', - }, - }, - 'ignored': {}, - }, - }, - ) - - kg_mgr = SrvKGResourceMgr(('./json_folder',)) - kg_mgr.import_resource() - out, err = capsys.readouterr() - assert out == ( - 'File imported: \njson_test/fake_file1.json, \n' 'json_test/fake_file2.json, \njson_test/fake_file3.json\n' - ) - - -def test_import_kg_invalid_json_too_large(mocker, capsys): - mocker.patch('app.services.user_authentication.token_manager.SrvTokenManager.check_valid', return_value=0) - mocker.patch('os.path.isdir', return_value=False) - mocker.patch('os.path.getsize', return_value=1000001) - kg_mgr = SrvKGResourceMgr(('fake_file.json',)) - kg_mgr.import_resource() - out, err = capsys.readouterr() - assert out == 'fake_file.json is too large\n' From 0128881c63380c858f5315965f4febccb97b8af5 Mon Sep 17 00:00:00 2001 From: Vadym Moshynskyi Date: Fri, 14 Jul 2023 15:00:23 +0200 Subject: [PATCH 08/12] PILOT-3306: Remove use_config command functionality (#76) * PILOT-3306: Remove use_config command functionality * PILOT-3306: Remove lineage leftovers --- app/commands/entry_point.py | 15 +--------- app/commands/use_config.py | 29 ------------------- app/configs/app_config.py | 1 - app/resources/custom_error.py | 8 +---- app/services/output_manager/error_handler.py | 3 -- app/services/output_manager/help_page.py | 10 ------- app/services/user_authentication/decorator.py | 10 ------- .../user_authentication/user_set_config.py | 27 ----------------- env.py | 1 - 9 files changed, 2 insertions(+), 102 deletions(-) delete mode 100644 app/commands/use_config.py delete mode 100644 app/services/user_authentication/user_set_config.py diff --git a/app/commands/entry_point.py b/app/commands/entry_point.py index e9ed194c..757e42b7 100644 --- a/app/commands/entry_point.py +++ b/app/commands/entry_point.py @@ -6,7 +6,6 @@ import click -from app.services.user_authentication.decorator import require_config from app.services.user_authentication.decorator import require_login_session from .container_registry import create_project @@ -26,7 +25,6 @@ # Import custom commands from .project import project_list_all -from .use_config import set_env from .user import login from .user import logout @@ -34,7 +32,7 @@ def command_groups(): - commands = ['file', 'user', 'use_config', 'project', 'dataset'] + commands = ['file', 'user', 'project', 'dataset'] if container_registry_enabled: commands.append('container_registry') return commands @@ -46,37 +44,28 @@ def entry_point(): @entry_point.group(name='project') -@require_config @require_login_session def project_group(): pass @entry_point.group(name='dataset') -@require_config @require_login_session def dataset_group(): pass @entry_point.group(name='file') -@require_config @require_login_session def file_group(): pass @entry_point.group(name='user') -@require_config def user_group(): pass -@entry_point.group(name='use_config') -def config_group(): - pass - - file_group.add_command(file_put) file_group.add_command(file_check_manifest) file_group.add_command(file_export_manifest) @@ -89,13 +78,11 @@ def config_group(): dataset_group.add_command(dataset_list) dataset_group.add_command(dataset_show_detail) dataset_group.add_command(dataset_download) -config_group.add_command(set_env) # Custom commands if container_registry_enabled: @entry_point.group(name='container_registry') - @require_config def cr_group(): pass diff --git a/app/commands/use_config.py b/app/commands/use_config.py deleted file mode 100644 index cabe07fd..00000000 --- a/app/commands/use_config.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (C) 2022-2023 Indoc Research -# -# Contact Indoc Research for any questions regarding the use of this source code. - -import click - -import app.services.output_manager.help_page as config_help -from app.services.user_authentication.user_set_config import set_config -from app.utils.aggregated import doc - - -@click.command() -def cli(): - """Config Actions.""" - pass - - -@click.command() -@click.argument('path', type=click.Path(exists=True), nargs=1) -@click.option( - '-o', - '--output', - type=click.Path(), - default='.', - help=config_help.config_help_page(config_help.ConfigHELP.CONFIG_DESTINATION), -) -@doc(config_help.config_help_page(config_help.ConfigHELP.SET_CONFIG)) -def set_env(path, output): - set_config(path, output) diff --git a/app/configs/app_config.py b/app/configs/app_config.py index 52646fe4..f7a388a2 100644 --- a/app/configs/app_config.py +++ b/app/configs/app_config.py @@ -45,7 +45,6 @@ class Connections: url_upload_greenroom = ConfigClass.url_upload_greenroom url_upload_core = ConfigClass.url_upload_core url_status = ConfigClass.url_status - url_lineage = ConfigClass.url_lineage url_download_greenroom = ConfigClass.url_download_greenroom url_download_core = ConfigClass.url_download_core url_v2_download_pre = ConfigClass.url_v2_download_pre diff --git a/app/resources/custom_error.py b/app/resources/custom_error.py index afb6b512..10d594d4 100644 --- a/app/resources/custom_error.py +++ b/app/resources/custom_error.py @@ -36,7 +36,6 @@ class Error: 'INVALID_ATTRIBUTE': "Invalid attribute '%s'. Please verify and try again.", 'INVALID_UPLOAD_REQUEST': 'Invalid upload request: %s', 'INVALID_SOURCE_FILE': 'File does not exist or source file provided is invalid: %s', - 'INVALID_LINEAGE': 'Create lineage failed: %s', 'INVALID_PIPELINENAME': ( 'Invalid pipeline name. Pipeline names must be between 1 and 20 characters long and ' 'may only contain lowercase letters, numbers, and/or special characters of -_, .' @@ -66,7 +65,7 @@ class Error: 'It means the resumable file is not the same with previous one.\n' 'Please to double check the file content.' ), - 'UNSUPPORT_TAG_MANIFEST': 'Tagging, lineage and manifest attaching are not supported for folder type.', + 'UNSUPPORT_TAG_MANIFEST': 'Tagging and manifest attaching are not supported for folder type.', 'INVALID_INPUT': 'Invalid input. Please try again.', 'UNSUPPORTED_PROJECT': 'This function is not supported in the given Project %s', 'CREATE_FOLDER_IF_NOT_EXIST': 'Target folder does not exist. Would you like to create a new folder?', @@ -112,11 +111,6 @@ class Error: 'sensitive identifiers, please cancel this transfer and upload the data to the Green Room to perform ' 'these actions.' ), - 'CONFIG_NOT_FOUND': 'This cli is not setup properly, please download config file and config again.', - 'CONFIG_EXIST': ( - 'This cli has been configured already.' - 'If you want to re-config this cli please remove previous file first' - ), 'CONFIG_INVALID_PERMISSIONS': 'Cannot proceed with current config permissions.\n%s', 'CONTAINER_REGISTRY_NO_URL': ( 'Container registry has not yet been configured. Related commands cannot be used at this time.' diff --git a/app/services/output_manager/error_handler.py b/app/services/output_manager/error_handler.py index 452b812e..e79ee130 100644 --- a/app/services/output_manager/error_handler.py +++ b/app/services/output_manager/error_handler.py @@ -29,7 +29,6 @@ class ECustomizedError(enum.Enum): MISSING_REQUIRED_ATTRIBUTE = 'MISSING_REQUIRED_ATTRIBUTE' INVALID_UPLOAD_REQUEST = 'INVALID_UPLOAD_REQUEST' INVALID_SOURCE_FILE = 'INVALID_SOURCE_FILE' - INVALID_LINEAGE = 'INVALID_LINEAGE' INVALID_PIPELINENAME = 'INVALID_PIPELINENAME' INVALID_PATHS = 'INVALID_PATHS' INVALID_RESUMABLE = 'INVALID_RESUMABLE' @@ -77,8 +76,6 @@ class ECustomizedError(enum.Enum): USER_NOT_FOUND = 'USER_NOT_FOUND' CONTAINER_REGISTRY_OTHER = 'CONTAINER_REGISTRY_OTHER' CONTAINER_REGISTRY_NO_URL = 'CONTAINER_REGISTRY_NO_URL' - CONFIG_NOT_FOUND = 'CONFIG_NOT_FOUND' - CONFIG_EXIST = 'CONFIG_EXIST' CONFIG_INVALID_PERMISSIONS = 'CONFIG_INVALID_PERMISSIONS' diff --git a/app/services/output_manager/help_page.py b/app/services/output_manager/help_page.py index 033c16ce..17793d71 100644 --- a/app/services/output_manager/help_page.py +++ b/app/services/output_manager/help_page.py @@ -37,16 +37,6 @@ def project_help_page(ProjectHELP: ProjectHELP): return helps.get(ProjectHELP.name) -class ConfigHELP(enum.Enum): - SET_CONFIG = 'SET_CONFIG' - CONFIG_DESTINATION = 'CONFIG_DESTINATION' - - -def config_help_page(ConfigHELP: ConfigHELP): - helps = help_msg.get('config', 'default config help') - return helps.get(ConfigHELP.name) - - class UserHELP(enum.Enum): USER_LOGIN = 'USER_LOGIN' USER_LOGOUT = 'USER_LOGOUT' diff --git a/app/services/user_authentication/decorator.py b/app/services/user_authentication/decorator.py index e3508df5..1b485e0a 100644 --- a/app/services/user_authentication/decorator.py +++ b/app/services/user_authentication/decorator.py @@ -11,7 +11,6 @@ from .token_manager import SrvTokenManager from .user_login_logout import check_is_active from .user_login_logout import check_is_login -from .user_set_config import check_config def require_valid_token(azp=AppConfig.Env.keycloak_device_client_id): @@ -53,12 +52,3 @@ def decorated(*args, **kwargs): return func(*args, **kwargs) return decorated - - -def require_config(func): - @wraps(func) - def decorated(*args, **kwargs): - check_config() - return func(*args, **kwargs) - - return decorated diff --git a/app/services/user_authentication/user_set_config.py b/app/services/user_authentication/user_set_config.py deleted file mode 100644 index faf8def2..00000000 --- a/app/services/user_authentication/user_set_config.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (C) 2022-2023 Indoc Research -# -# Contact Indoc Research for any questions regarding the use of this source code. - -import os -import shutil - -from app.configs import app_config -from app.services.logger_services import log_functions as logger -from app.services.output_manager.error_handler import ECustomizedError -from app.services.output_manager.error_handler import SrvErrorHandler - - -def check_config(): - connections = app_config.AppConfig.Connections.__dict__ - for k, v in connections.items(): - if k.startswith('url') and not v: - SrvErrorHandler.customized_handle(ECustomizedError.CONFIG_NOT_FOUND, True) - - -def set_config(target_path, destination): - config_path = os.path.join(destination, '.env') - if os.path.isfile(config_path): - SrvErrorHandler.customized_handle(ECustomizedError.CONFIG_EXIST, True) - else: - shutil.copy(target_path, destination) - logger.succeed('config file set') diff --git a/env.py b/env.py index 90f67c74..551c322d 100644 --- a/env.py +++ b/env.py @@ -43,7 +43,6 @@ def modify_values(self, settings): settings.url_dataset_v2download = settings.base_url + 'portal/download/core/v2/dataset' settings.url_dataset = settings.base_url + 'portal/v1/dataset' settings.url_validation = settings.base_url + 'v1/files/validation' - settings.url_lineage = settings.url_bff + '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/v1/lineage' return settings From 9ec1b0fbc254e7261034017c38c99c7556438db0 Mon Sep 17 00:00:00 2001 From: Color Zhan Date: Mon, 17 Jul 2023 16:09:26 -0400 Subject: [PATCH 09/12] remove the related lineage related code (#74) Co-authored-by: zhiren From f5e579c15db1e7165962f9b8b76ea883878cb507 Mon Sep 17 00:00:00 2001 From: Vadym Moshynskyi Date: Thu, 20 Jul 2023 10:11:11 +0200 Subject: [PATCH 10/12] PILOT-3306: Refactor settings usage (#77) * PILOT-3306: Refactor settings usage * PILOT-3306: Fix issue with not found file in search --- .github/workflows/run-tests.yml | 2 +- app/configs/app_config.py | 18 +- app/configs/config.py | 107 ++ app/configs/user_config.py | 6 +- .../file_manager/file_upload/upload_client.py | 3 +- app/services/user_authentication/decorator.py | 11 +- .../user_authentication/token_manager.py | 7 +- .../user_authentication/user_login_logout.py | 9 +- app/utils/aggregated.py | 6 +- env.py | 74 -- poetry.lock | 913 ++++-------------- pyproject.toml | 5 +- tests/app/commands/test_user.py | 7 +- .../file_upload/test_file_upload.py | 4 +- .../file_upload/test_upload_client.py | 4 +- .../user_authentication/test_token_manager.py | 5 +- tests/app/utils/test_aggregated.py | 17 +- tests/conftest.py | 10 +- 18 files changed, 343 insertions(+), 865 deletions(-) create mode 100644 app/configs/config.py delete mode 100644 env.py diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 82399a9a..73ae9ac0 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -47,7 +47,7 @@ jobs: uses: pre-commit/action@v3.0.0 - name: Run tests - run: poetry run pytest -vvv --exitfirst --cov=app --cov-report=term --cov-report=xml --cov-fail-under=54 + run: poetry run pytest -vvv --exitfirst --cov=app --cov-report=term --cov-report=xml --cov-fail-under=60 - name: Coverage report comment uses: mishakav/pytest-coverage-comment@v1.1.42 diff --git a/app/configs/app_config.py b/app/configs/app_config.py index f7a388a2..cf72cadf 100644 --- a/app/configs/app_config.py +++ b/app/configs/app_config.py @@ -2,16 +2,11 @@ # # Contact Indoc Research for any questions regarding the use of this source code. -from env import ConfigClass +from app.configs.config import ConfigClass class AppConfig: class Env: - section = 'environment' - project = ConfigClass.project - user_config_path = ConfigClass.config_path - msg_path = ConfigClass.custom_path - user_config_file = 'config.ini' token_warn_need_refresh = 120 # refresh token if token is about to expire token_refresh_interval = 90 # auto refresh token every 40 seconds @@ -23,21 +18,16 @@ class Env: resilient_backoff = 1 resilient_retry_interval = 1 # seconds resilient_retry_code = [502, 503, 504, 404, 401] - pipeline_straight_upload = f'{project}cli_upload' - default_upload_message = f'{project}cli straight uploaded' + pipeline_straight_upload = f'{ConfigClass.project}cli_upload' + default_upload_message = f'{ConfigClass.project}cli straight uploaded' session_duration = 3600.0 upload_batch_size = 100 - harbor_client_secret = ConfigClass.harbor_client_secret core_zone = 'core' green_zone = 'greenroom' core_bucket_prefix = 'core' greenroom_bucket_prefix = 'gr' - keycloak_device_client_id = ConfigClass.keycloak_device_client_id - keycloak_api_key_audience = ConfigClass.keycloak_api_key_audience - class Connections: - section = 'connections' url_harbor = ConfigClass.url_harbor url_authn = ConfigClass.url_authn url_refresh_token = ConfigClass.url_refresh_token @@ -53,7 +43,5 @@ class Connections: url_validation = ConfigClass.url_validation url_keycloak = ConfigClass.url_keycloak url_keycloak_token = f'{ConfigClass.url_keycloak}/token' - url_keycloak_realm = ConfigClass.url_keycloak.rstrip('/').replace('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/protocol/openid-connect', '') url_bff = ConfigClass.url_bff - # add url_base to check if value exist url_base = ConfigClass.base_url diff --git a/app/configs/config.py b/app/configs/config.py new file mode 100644 index 00000000..ce291796 --- /dev/null +++ b/app/configs/config.py @@ -0,0 +1,107 @@ +# Copyright (C) 2022-2023 Indoc Research +# +# Contact Indoc Research for any questions regarding the use of this source code. + +from functools import lru_cache +from pathlib import Path +from typing import Set + +from pydantic import computed_field +from pydantic_settings import BaseSettings +from pydantic_settings import SettingsConfigDict + + +class Settings(BaseSettings): + model_config = SettingsConfigDict(env_file='.env', extra='allow') + + project: str = 'pilot' + app_name: str = 'pilotcli' + + @computed_field + def config_path(self) -> str: + return str(Path.home() / f'.{self.app_name}') + + config_file: str = 'config.ini' + + keycloak_device_client_id: str = 'cli' + keycloak_api_key_audience: Set[str] = {'api-key'} + + vm_info: str = '' + + harbor_client_secret: str = '' + url_harbor: str = '' + + domain: str = 'pilot.indocresearch.com' + + @computed_field + def base_url(self) -> str: + return f'https://api.{self.domain}/pilot' + + @computed_field + def url_bff(self) -> str: + return f'{self.base_url}/cli' + + @computed_field + def url_keycloak_realm(self) -> str: + return f'https://iam.{self.domain}/realms/pilot' + + @computed_field + def url_keycloak(self) -> str: + return f'{self.url_keycloak_realm}/protocol/openid-connect' + + @computed_field + def url_authn(self) -> str: + return f'{self.base_url}/portal/users/auth' + + @computed_field + def url_refresh_token(self) -> str: + return f'{self.base_url}/portal/users/refresh' + + @computed_field + def url_file_tag(self) -> str: + return f'{self.base_url}/portal/v2/%s/tags' + + @computed_field + def url_upload_greenroom(self) -> str: + return f'{self.base_url}/upload/gr' + + @computed_field + def url_upload_core(self) -> str: + return f'{self.base_url}/upload/core' + + @computed_field + def url_status(self) -> str: + return f'{self.base_url}/portal/v1/files/actions/tasks' + + @computed_field + def url_download_greenroom(self) -> str: + return f'{self.base_url}/portal/download/gr/' + + @computed_field + def url_download_core(self) -> str: + return f'{self.base_url}/portal/download/core/' + + @computed_field + def url_v2_download_pre(self) -> str: + return f'{self.url_bff}/v1/project/%s/files/download' + + @computed_field + def url_dataset_v2download(self) -> str: + return f'{self.base_url}/portal/download/core/v2/dataset' + + @computed_field + def url_dataset(self) -> str: + return f'{self.base_url}/portal/v1/dataset' + + @computed_field + def url_validation(self) -> str: + return f'{self.base_url}/v1/files/validation' + + +@lru_cache(1) +def get_settings() -> Settings: + settings = Settings() + return settings + + +ConfigClass = get_settings() diff --git a/app/configs/user_config.py b/app/configs/user_config.py index 93c7170b..3677832a 100644 --- a/app/configs/user_config.py +++ b/app/configs/user_config.py @@ -10,7 +10,7 @@ from typing import Iterable from typing import Union -from app.configs.app_config import AppConfig +from app.configs.config import ConfigClass from app.models.singleton import Singleton from app.services.crypto.crypto import decryption from app.services.crypto.crypto import encryption @@ -28,9 +28,9 @@ class UserConfig(metaclass=Singleton): def __init__(self, config_path: Union[str, Path, None] = None, config_filename: Union[str, None] = None) -> None: if config_path is None: - config_path = AppConfig.Env.user_config_path + config_path = ConfigClass.config_path if config_filename is None: - config_filename = AppConfig.Env.user_config_file + config_filename = ConfigClass.config_file config_path = Path(config_path) if not config_path.exists(): diff --git a/app/services/file_manager/file_upload/upload_client.py b/app/services/file_manager/file_upload/upload_client.py index f1e7ffb8..1e1ebbac 100644 --- a/app/services/file_manager/file_upload/upload_client.py +++ b/app/services/file_manager/file_upload/upload_client.py @@ -18,6 +18,7 @@ import app.services.output_manager.message_handler as mhandler from app.configs.app_config import AppConfig +from app.configs.config import ConfigClass from app.configs.user_config import UserConfig from app.models.upload_form import generate_on_success_form from app.services.file_manager.file_upload.models import FileObject @@ -413,7 +414,7 @@ def check_status(self, file_object: FileObject) -> bool: def set_finish_upload(self): self.finish_upload = True - def upload_token_refresh(self, azp: str = AppConfig.Env.keycloak_device_client_id): + def upload_token_refresh(self, azp: str = ConfigClass.keycloak_device_client_id): token_manager = SrvTokenManager() DEFAULT_INTERVAL = 2 # seconds to check if the upload is finished total_count = 0 # when total_count equals token_refresh_interval, refresh token diff --git a/app/services/user_authentication/decorator.py b/app/services/user_authentication/decorator.py index 1b485e0a..a69173e1 100644 --- a/app/services/user_authentication/decorator.py +++ b/app/services/user_authentication/decorator.py @@ -4,16 +4,15 @@ from functools import wraps -from app.configs.app_config import AppConfig +from app.configs.config import ConfigClass from app.services.output_manager.error_handler import ECustomizedError from app.services.output_manager.error_handler import SrvErrorHandler +from app.services.user_authentication.token_manager import SrvTokenManager +from app.services.user_authentication.user_login_logout import check_is_active +from app.services.user_authentication.user_login_logout import check_is_login -from .token_manager import SrvTokenManager -from .user_login_logout import check_is_active -from .user_login_logout import check_is_login - -def require_valid_token(azp=AppConfig.Env.keycloak_device_client_id): +def require_valid_token(azp=ConfigClass.keycloak_device_client_id): def decorate(func): @wraps(func) def decorated(*args, **kwargs): diff --git a/app/services/user_authentication/token_manager.py b/app/services/user_authentication/token_manager.py index 90283f9b..80b4aa0e 100644 --- a/app/services/user_authentication/token_manager.py +++ b/app/services/user_authentication/token_manager.py @@ -8,6 +8,7 @@ import requests from app.configs.app_config import AppConfig +from app.configs.config import ConfigClass from app.configs.user_config import UserConfig from app.models.enums import LoginMethod from app.models.service_meta_class import MetaService @@ -44,7 +45,7 @@ def is_api_key(self) -> bool: audience = token['aud'] if isinstance(audience, str): audience = [audience] - return AppConfig.Env.keycloak_api_key_audience.issubset(set(audience)) + return ConfigClass.keycloak_api_key_audience.issubset(set(audience)) def check_valid(self, required_azp): """ @@ -64,7 +65,7 @@ def check_valid(self, required_azp): # ``kong`` is hardcoded in the decorator definition as default value. azp_token_condition = decoded_access_token['azp'] not in [ required_azp, - AppConfig.Env.keycloak_device_client_id, + ConfigClass.keycloak_device_client_id, ] if azp_token_condition or expiry_at <= now: @@ -86,7 +87,7 @@ def refresh(self, azp: str) -> None: } if azp == 'harbor': - payload.update({'client_id': AppConfig.Env.harbor_client_secret}) + payload.update({'client_id': ConfigClass.harbor_client_secret}) headers = {'Content-Type': 'application/x-www-form-urlencoded'} response = requests.post(url, data=payload, headers=headers) diff --git a/app/services/user_authentication/user_login_logout.py b/app/services/user_authentication/user_login_logout.py index 0cad5c5f..a66b1ae0 100644 --- a/app/services/user_authentication/user_login_logout.py +++ b/app/services/user_authentication/user_login_logout.py @@ -13,6 +13,7 @@ from requests import RequestException from app.configs.app_config import AppConfig +from app.configs.config import ConfigClass from app.configs.user_config import UserConfig from app.services.output_manager.error_handler import ECustomizedError from app.services.output_manager.error_handler import SrvErrorHandler @@ -22,7 +23,7 @@ def exchange_api_key(api_key: str) -> Union[str, None]: """Exchange API Key with JWT token using Keycloak.""" - url = f'{AppConfig.Connections.url_keycloak_realm}/api-key/{api_key}' + url = f'{ConfigClass.url_keycloak_realm}/api-key/{api_key}' try: response = requests.get(url, timeout=5) response.raise_for_status() @@ -59,7 +60,7 @@ def user_device_id_login() -> Dict[str, Any]: url = f'{AppConfig.Connections.url_keycloak}/auth/device' headers = {'Content-Type': 'application/x-www-form-urlencoded'} - data = {'client_id': AppConfig.Env.keycloak_device_client_id} + data = {'client_id': ConfigClass.keycloak_device_client_id} resp = requests.post(url, headers=headers, data=data) if resp.status_code == 200: device_data = resp.json() @@ -80,7 +81,7 @@ def validate_user_device_login(device_code: str, expires: int, interval: int) -> headers = {'Content-Type': 'application/x-www-form-urlencoded'} data = { 'device_code': device_code, - 'client_id': AppConfig.Env.keycloak_device_client_id, + 'client_id': ConfigClass.keycloak_device_client_id, 'grant_type': 'urn:ietf:params:oauth:grant-type:device_code', } waiting_result = True @@ -162,7 +163,7 @@ def request_harbor_tokens(username, password): 'username': username, 'password': password, 'client_id': 'harbor', - 'client_secret': AppConfig.Env.harbor_client_secret, + 'client_secret': ConfigClass.harbor_client_secret, } headers = {'Content-Type': 'application/x-www-form-urlencoded'} response = requests.post(url, data=payload, headers=headers, verify=False) diff --git a/app/utils/aggregated.py b/app/utils/aggregated.py index 099339af..5fcbaab5 100644 --- a/app/utils/aggregated.py +++ b/app/utils/aggregated.py @@ -10,16 +10,16 @@ import requests from app.configs.app_config import AppConfig +from app.configs.config import ConfigClass from app.configs.user_config import UserConfig from app.services.output_manager.error_handler import ECustomizedError from app.services.output_manager.error_handler import SrvErrorHandler from app.services.user_authentication.decorator import require_valid_token -from env import ConfigClass def resilient_session(): # each resilient session will - headers = {'VM-Info': ConfigClass.VM_INFO} + headers = {'VM-Info': ConfigClass.vm_info} client = httpx.Client(headers=headers, timeout=None) return client @@ -39,6 +39,8 @@ def search_item(project_code, zone, folder_relative_path, item_type, container_t res = requests.get(url, params=params, headers=headers) if res.status_code == 403: SrvErrorHandler.customized_handle(ECustomizedError.PERMISSION_DENIED, project_code) + elif res.status_code == 404: + pass elif res.status_code != 200: SrvErrorHandler.default_handle(res.text, True) diff --git a/env.py b/env.py deleted file mode 100644 index 551c322d..00000000 --- a/env.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (C) 2022-2023 Indoc Research -# -# Contact Indoc Research for any questions regarding the use of this source code. - -import os -from typing import Set - -from dotenv import load_dotenv -from pydantic import BaseSettings -from pydantic import Extra - -load_dotenv() -load_dotenv('/etc/environment') - - -class Settings(BaseSettings): - env = 'dev' - project = 'pilot' - app_name = 'pilotcli' - config_path = '{}/.{}cli/'.format(os.environ.get('HOME') or os.environ.get('HOMEPATH'), project) - custom_path = 'app/resources' - harbor_client_secret: str = '' - base_url: str = '' - url_harbor: str = '' - url_bff: str = '' - url_keycloak: str = '' - - keycloak_device_client_id: str = 'cli_test2' - keycloak_api_key_audience: Set[str] = {'api-key'} - - VM_INFO: str = '' - - def modify_values(self, settings): - settings.url_authn = settings.base_url + 'portal/users/auth' - settings.url_refresh_token = settings.base_url + 'portal/users/refresh' - settings.url_file_tag = settings.base_url + 'portal/v2/%s/tags' - settings.url_upload_greenroom = settings.base_url + 'upload/gr' - settings.url_upload_core = settings.base_url + 'upload/core' - settings.url_status = settings.base_url + 'portal/v1/files/actions/tasks' - settings.url_download_greenroom = settings.base_url + 'portal/download/gr/' - settings.url_download_core = settings.base_url + 'portal/download/core/' - settings.url_v2_download_pre = settings.url_bff + '/v1/project/%s/files/download' - settings.url_dataset_v2download = settings.base_url + 'portal/download/core/v2/dataset' - settings.url_dataset = settings.base_url + 'portal/v1/dataset' - settings.url_validation = settings.base_url + 'v1/files/validation' - - return settings - - class Config: - env_file = '.env' - env_file_encoding = 'utf-8' - extra = Extra.allow - - @classmethod - def customise_sources( - cls, - init_settings, - env_settings, - file_secret_settings, - ): - return ( - env_settings, - init_settings, - file_secret_settings, - ) - - -def get_settings(): - settings = Settings() - settings = settings.modify_values(settings) - return settings - - -ConfigClass = get_settings() diff --git a/poetry.lock b/poetry.lock index 6c6eb6a7..397f8d2a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,217 +1,31 @@ # This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. [[package]] -name = "aioboto3" -version = "9.6.0" -description = "Async boto3 wrapper" -category = "main" -optional = false -python-versions = ">=3.7,<4.0" -files = [ - {file = "aioboto3-9.6.0-py3-none-any.whl", hash = "sha256:a62de3203c7372b5f1247057b9705eeb26c45ee65b5d1fab91767e4f31fd7b47"}, - {file = "aioboto3-9.6.0.tar.gz", hash = "sha256:abac5dcfa871627b7040e6586a69e3359e2dfc4e15dc66135969f2d26fbdcb3b"}, -] - -[package.dependencies] -aiobotocore = {version = "2.3.0", extras = ["boto3"]} - -[package.extras] -chalice = ["chalice (>=1.24.0)"] -s3cse = ["cryptography (>=2.3.1)"] - -[[package]] -name = "aiobotocore" -version = "2.3.0" -description = "Async client for aws services using botocore and aiohttp" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "aiobotocore-2.3.0.tar.gz", hash = "sha256:fc3d3d6061410bc194d09ec545e30b4469d5f74770f937aca5f6aa45e62a1bfe"}, -] - -[package.dependencies] -aiohttp = ">=3.3.1" -aioitertools = ">=0.5.1" -boto3 = {version = ">=1.21.21,<1.21.22", optional = true, markers = "extra == \"boto3\""} -botocore = ">=1.24.21,<1.24.22" -wrapt = ">=1.10.10" - -[package.extras] -awscli = ["awscli (>=1.22.76,<1.22.77)"] -boto3 = ["boto3 (>=1.21.21,<1.21.22)"] - -[[package]] -name = "aiohttp" -version = "3.8.3" -description = "Async http client/server framework (asyncio)" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "aiohttp-3.8.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ba71c9b4dcbb16212f334126cc3d8beb6af377f6703d9dc2d9fb3874fd667ee9"}, - {file = "aiohttp-3.8.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d24b8bb40d5c61ef2d9b6a8f4528c2f17f1c5d2d31fed62ec860f6006142e83e"}, - {file = "aiohttp-3.8.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f88df3a83cf9df566f171adba39d5bd52814ac0b94778d2448652fc77f9eb491"}, - {file = "aiohttp-3.8.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b97decbb3372d4b69e4d4c8117f44632551c692bb1361b356a02b97b69e18a62"}, - {file = "aiohttp-3.8.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:309aa21c1d54b8ef0723181d430347d7452daaff93e8e2363db8e75c72c2fb2d"}, - {file = "aiohttp-3.8.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad5383a67514e8e76906a06741febd9126fc7c7ff0f599d6fcce3e82b80d026f"}, - {file = "aiohttp-3.8.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20acae4f268317bb975671e375493dbdbc67cddb5f6c71eebdb85b34444ac46b"}, - {file = "aiohttp-3.8.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05a3c31c6d7cd08c149e50dc7aa2568317f5844acd745621983380597f027a18"}, - {file = "aiohttp-3.8.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d6f76310355e9fae637c3162936e9504b4767d5c52ca268331e2756e54fd4ca5"}, - {file = "aiohttp-3.8.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:256deb4b29fe5e47893fa32e1de2d73c3afe7407738bd3c63829874661d4822d"}, - {file = "aiohttp-3.8.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:5c59fcd80b9049b49acd29bd3598cada4afc8d8d69bd4160cd613246912535d7"}, - {file = "aiohttp-3.8.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:059a91e88f2c00fe40aed9031b3606c3f311414f86a90d696dd982e7aec48142"}, - {file = "aiohttp-3.8.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2feebbb6074cdbd1ac276dbd737b40e890a1361b3cc30b74ac2f5e24aab41f7b"}, - {file = "aiohttp-3.8.3-cp310-cp310-win32.whl", hash = "sha256:5bf651afd22d5f0c4be16cf39d0482ea494f5c88f03e75e5fef3a85177fecdeb"}, - {file = "aiohttp-3.8.3-cp310-cp310-win_amd64.whl", hash = "sha256:653acc3880459f82a65e27bd6526e47ddf19e643457d36a2250b85b41a564715"}, - {file = "aiohttp-3.8.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:86fc24e58ecb32aee09f864cb11bb91bc4c1086615001647dbfc4dc8c32f4008"}, - {file = "aiohttp-3.8.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75e14eac916f024305db517e00a9252714fce0abcb10ad327fb6dcdc0d060f1d"}, - {file = "aiohttp-3.8.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d1fde0f44029e02d02d3993ad55ce93ead9bb9b15c6b7ccd580f90bd7e3de476"}, - {file = "aiohttp-3.8.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ab94426ddb1ecc6a0b601d832d5d9d421820989b8caa929114811369673235c"}, - {file = "aiohttp-3.8.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89d2e02167fa95172c017732ed7725bc8523c598757f08d13c5acca308e1a061"}, - {file = "aiohttp-3.8.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:02f9a2c72fc95d59b881cf38a4b2be9381b9527f9d328771e90f72ac76f31ad8"}, - {file = "aiohttp-3.8.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c7149272fb5834fc186328e2c1fa01dda3e1fa940ce18fded6d412e8f2cf76d"}, - {file = "aiohttp-3.8.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:512bd5ab136b8dc0ffe3fdf2dfb0c4b4f49c8577f6cae55dca862cd37a4564e2"}, - {file = "aiohttp-3.8.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7018ecc5fe97027214556afbc7c502fbd718d0740e87eb1217b17efd05b3d276"}, - {file = "aiohttp-3.8.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:88c70ed9da9963d5496d38320160e8eb7e5f1886f9290475a881db12f351ab5d"}, - {file = "aiohttp-3.8.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:da22885266bbfb3f78218dc40205fed2671909fbd0720aedba39b4515c038091"}, - {file = "aiohttp-3.8.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:e65bc19919c910127c06759a63747ebe14f386cda573d95bcc62b427ca1afc73"}, - {file = "aiohttp-3.8.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:08c78317e950e0762c2983f4dd58dc5e6c9ff75c8a0efeae299d363d439c8e34"}, - {file = "aiohttp-3.8.3-cp311-cp311-win32.whl", hash = "sha256:45d88b016c849d74ebc6f2b6e8bc17cabf26e7e40c0661ddd8fae4c00f015697"}, - {file = "aiohttp-3.8.3-cp311-cp311-win_amd64.whl", hash = "sha256:96372fc29471646b9b106ee918c8eeb4cca423fcbf9a34daa1b93767a88a2290"}, - {file = "aiohttp-3.8.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c971bf3786b5fad82ce5ad570dc6ee420f5b12527157929e830f51c55dc8af77"}, - {file = "aiohttp-3.8.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff25f48fc8e623d95eca0670b8cc1469a83783c924a602e0fbd47363bb54aaca"}, - {file = "aiohttp-3.8.3-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e381581b37db1db7597b62a2e6b8b57c3deec95d93b6d6407c5b61ddc98aca6d"}, - {file = "aiohttp-3.8.3-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:db19d60d846283ee275d0416e2a23493f4e6b6028825b51290ac05afc87a6f97"}, - {file = "aiohttp-3.8.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25892c92bee6d9449ffac82c2fe257f3a6f297792cdb18ad784737d61e7a9a85"}, - {file = "aiohttp-3.8.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:398701865e7a9565d49189f6c90868efaca21be65c725fc87fc305906be915da"}, - {file = "aiohttp-3.8.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:4a4fbc769ea9b6bd97f4ad0b430a6807f92f0e5eb020f1e42ece59f3ecfc4585"}, - {file = "aiohttp-3.8.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:b29bfd650ed8e148f9c515474a6ef0ba1090b7a8faeee26b74a8ff3b33617502"}, - {file = "aiohttp-3.8.3-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:1e56b9cafcd6531bab5d9b2e890bb4937f4165109fe98e2b98ef0dcfcb06ee9d"}, - {file = "aiohttp-3.8.3-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:ec40170327d4a404b0d91855d41bfe1fe4b699222b2b93e3d833a27330a87a6d"}, - {file = "aiohttp-3.8.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:2df5f139233060578d8c2c975128fb231a89ca0a462b35d4b5fcf7c501ebdbe1"}, - {file = "aiohttp-3.8.3-cp36-cp36m-win32.whl", hash = "sha256:f973157ffeab5459eefe7b97a804987876dd0a55570b8fa56b4e1954bf11329b"}, - {file = "aiohttp-3.8.3-cp36-cp36m-win_amd64.whl", hash = "sha256:437399385f2abcd634865705bdc180c8314124b98299d54fe1d4c8990f2f9494"}, - {file = "aiohttp-3.8.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:09e28f572b21642128ef31f4e8372adb6888846f32fecb288c8b0457597ba61a"}, - {file = "aiohttp-3.8.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f3553510abdbec67c043ca85727396ceed1272eef029b050677046d3387be8d"}, - {file = "aiohttp-3.8.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e168a7560b7c61342ae0412997b069753f27ac4862ec7867eff74f0fe4ea2ad9"}, - {file = "aiohttp-3.8.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:db4c979b0b3e0fa7e9e69ecd11b2b3174c6963cebadeecfb7ad24532ffcdd11a"}, - {file = "aiohttp-3.8.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e164e0a98e92d06da343d17d4e9c4da4654f4a4588a20d6c73548a29f176abe2"}, - {file = "aiohttp-3.8.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8a78079d9a39ca9ca99a8b0ac2fdc0c4d25fc80c8a8a82e5c8211509c523363"}, - {file = "aiohttp-3.8.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:21b30885a63c3f4ff5b77a5d6caf008b037cb521a5f33eab445dc566f6d092cc"}, - {file = "aiohttp-3.8.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4b0f30372cef3fdc262f33d06e7b411cd59058ce9174ef159ad938c4a34a89da"}, - {file = "aiohttp-3.8.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:8135fa153a20d82ffb64f70a1b5c2738684afa197839b34cc3e3c72fa88d302c"}, - {file = "aiohttp-3.8.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:ad61a9639792fd790523ba072c0555cd6be5a0baf03a49a5dd8cfcf20d56df48"}, - {file = "aiohttp-3.8.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:978b046ca728073070e9abc074b6299ebf3501e8dee5e26efacb13cec2b2dea0"}, - {file = "aiohttp-3.8.3-cp37-cp37m-win32.whl", hash = "sha256:0d2c6d8c6872df4a6ec37d2ede71eff62395b9e337b4e18efd2177de883a5033"}, - {file = "aiohttp-3.8.3-cp37-cp37m-win_amd64.whl", hash = "sha256:21d69797eb951f155026651f7e9362877334508d39c2fc37bd04ff55b2007091"}, - {file = "aiohttp-3.8.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ca9af5f8f5812d475c5259393f52d712f6d5f0d7fdad9acdb1107dd9e3cb7eb"}, - {file = "aiohttp-3.8.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d90043c1882067f1bd26196d5d2db9aa6d268def3293ed5fb317e13c9413ea4"}, - {file = "aiohttp-3.8.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d737fc67b9a970f3234754974531dc9afeea11c70791dcb7db53b0cf81b79784"}, - {file = "aiohttp-3.8.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebf909ea0a3fc9596e40d55d8000702a85e27fd578ff41a5500f68f20fd32e6c"}, - {file = "aiohttp-3.8.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5835f258ca9f7c455493a57ee707b76d2d9634d84d5d7f62e77be984ea80b849"}, - {file = "aiohttp-3.8.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da37dcfbf4b7f45d80ee386a5f81122501ec75672f475da34784196690762f4b"}, - {file = "aiohttp-3.8.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87f44875f2804bc0511a69ce44a9595d5944837a62caecc8490bbdb0e18b1342"}, - {file = "aiohttp-3.8.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:527b3b87b24844ea7865284aabfab08eb0faf599b385b03c2aa91fc6edd6e4b6"}, - {file = "aiohttp-3.8.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d5ba88df9aa5e2f806650fcbeedbe4f6e8736e92fc0e73b0400538fd25a4dd96"}, - {file = "aiohttp-3.8.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e7b8813be97cab8cb52b1375f41f8e6804f6507fe4660152e8ca5c48f0436017"}, - {file = "aiohttp-3.8.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:2dea10edfa1a54098703cb7acaa665c07b4e7568472a47f4e64e6319d3821ccf"}, - {file = "aiohttp-3.8.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:713d22cd9643ba9025d33c4af43943c7a1eb8547729228de18d3e02e278472b6"}, - {file = "aiohttp-3.8.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2d252771fc85e0cf8da0b823157962d70639e63cb9b578b1dec9868dd1f4f937"}, - {file = "aiohttp-3.8.3-cp38-cp38-win32.whl", hash = "sha256:66bd5f950344fb2b3dbdd421aaa4e84f4411a1a13fca3aeb2bcbe667f80c9f76"}, - {file = "aiohttp-3.8.3-cp38-cp38-win_amd64.whl", hash = "sha256:84b14f36e85295fe69c6b9789b51a0903b774046d5f7df538176516c3e422446"}, - {file = "aiohttp-3.8.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16c121ba0b1ec2b44b73e3a8a171c4f999b33929cd2397124a8c7fcfc8cd9e06"}, - {file = "aiohttp-3.8.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8d6aaa4e7155afaf994d7924eb290abbe81a6905b303d8cb61310a2aba1c68ba"}, - {file = "aiohttp-3.8.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:43046a319664a04b146f81b40e1545d4c8ac7b7dd04c47e40bf09f65f2437346"}, - {file = "aiohttp-3.8.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:599418aaaf88a6d02a8c515e656f6faf3d10618d3dd95866eb4436520096c84b"}, - {file = "aiohttp-3.8.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92a2964319d359f494f16011e23434f6f8ef0434acd3cf154a6b7bec511e2fb7"}, - {file = "aiohttp-3.8.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73a4131962e6d91109bca6536416aa067cf6c4efb871975df734f8d2fd821b37"}, - {file = "aiohttp-3.8.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:598adde339d2cf7d67beaccda3f2ce7c57b3b412702f29c946708f69cf8222aa"}, - {file = "aiohttp-3.8.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:75880ed07be39beff1881d81e4a907cafb802f306efd6d2d15f2b3c69935f6fb"}, - {file = "aiohttp-3.8.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a0239da9fbafd9ff82fd67c16704a7d1bccf0d107a300e790587ad05547681c8"}, - {file = "aiohttp-3.8.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4e3a23ec214e95c9fe85a58470b660efe6534b83e6cbe38b3ed52b053d7cb6ad"}, - {file = "aiohttp-3.8.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:47841407cc89a4b80b0c52276f3cc8138bbbfba4b179ee3acbd7d77ae33f7ac4"}, - {file = "aiohttp-3.8.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:54d107c89a3ebcd13228278d68f1436d3f33f2dd2af5415e3feaeb1156e1a62c"}, - {file = "aiohttp-3.8.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c37c5cce780349d4d51739ae682dec63573847a2a8dcb44381b174c3d9c8d403"}, - {file = "aiohttp-3.8.3-cp39-cp39-win32.whl", hash = "sha256:f178d2aadf0166be4df834c4953da2d7eef24719e8aec9a65289483eeea9d618"}, - {file = "aiohttp-3.8.3-cp39-cp39-win_amd64.whl", hash = "sha256:88e5be56c231981428f4f506c68b6a46fa25c4123a2e86d156c58a8369d31ab7"}, - {file = "aiohttp-3.8.3.tar.gz", hash = "sha256:3828fb41b7203176b82fe5d699e0d845435f2374750a44b480ea6b930f6be269"}, -] - -[package.dependencies] -aiosignal = ">=1.1.2" -async-timeout = ">=4.0.0a3,<5.0" -asynctest = {version = "0.13.0", markers = "python_version < \"3.8\""} -attrs = ">=17.3.0" -charset-normalizer = ">=2.0,<3.0" -frozenlist = ">=1.1.1" -multidict = ">=4.5,<7.0" -typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} -yarl = ">=1.0,<2.0" - -[package.extras] -speedups = ["Brotli", "aiodns", "cchardet"] - -[[package]] -name = "aioitertools" -version = "0.11.0" -description = "itertools and builtins for AsyncIO and mixed iterables" +name = "altgraph" +version = "0.17.3" +description = "Python graph (network) package" category = "main" optional = false -python-versions = ">=3.6" +python-versions = "*" files = [ - {file = "aioitertools-0.11.0-py3-none-any.whl", hash = "sha256:04b95e3dab25b449def24d7df809411c10e62aab0cbe31a50ca4e68748c43394"}, - {file = "aioitertools-0.11.0.tar.gz", hash = "sha256:42c68b8dd3a69c2bf7f2233bf7df4bb58b557bca5252ac02ed5187bbc67d6831"}, + {file = "altgraph-0.17.3-py2.py3-none-any.whl", hash = "sha256:c8ac1ca6772207179ed8003ce7687757c04b0b71536f81e2ac5755c6226458fe"}, + {file = "altgraph-0.17.3.tar.gz", hash = "sha256:ad33358114df7c9416cdb8fa1eaa5852166c505118717021c6a8c7c7abbd03dd"}, ] -[package.dependencies] -typing_extensions = {version = ">=4.0", markers = "python_version < \"3.10\""} - [[package]] -name = "aioredis" -version = "2.0.1" -description = "asyncio (PEP 3156) Redis support" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "aioredis-2.0.1-py3-none-any.whl", hash = "sha256:9ac0d0b3b485d293b8ca1987e6de8658d7dafcca1cddfcd1d506cae8cdebfdd6"}, - {file = "aioredis-2.0.1.tar.gz", hash = "sha256:eaa51aaf993f2d71f54b70527c440437ba65340588afeb786cd87c55c89cd98e"}, -] - -[package.dependencies] -async-timeout = "*" -typing-extensions = "*" - -[package.extras] -hiredis = ["hiredis (>=1.0)"] - -[[package]] -name = "aiosignal" -version = "1.3.1" -description = "aiosignal: a list of registered asynchronous callbacks" +name = "annotated-types" +version = "0.5.0" +description = "Reusable constraint types to use with typing.Annotated" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, - {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, + {file = "annotated_types-0.5.0-py3-none-any.whl", hash = "sha256:58da39888f92c276ad970249761ebea80ba544b77acddaa1a4d6cf78287d45fd"}, + {file = "annotated_types-0.5.0.tar.gz", hash = "sha256:47cdc3490d9ac1506ce92c7aaa76c579dc3509ff11e098fc867e5130ab7be802"}, ] [package.dependencies] -frozenlist = ">=1.1.0" - -[[package]] -name = "altgraph" -version = "0.17.3" -description = "Python graph (network) package" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "altgraph-0.17.3-py2.py3-none-any.whl", hash = "sha256:c8ac1ca6772207179ed8003ce7687757c04b0b71536f81e2ac5755c6226458fe"}, - {file = "altgraph-0.17.3.tar.gz", hash = "sha256:ad33358114df7c9416cdb8fa1eaa5852166c505118717021c6a8c7c7abbd03dd"}, -] +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""} [[package]] name = "anyio" @@ -235,33 +49,6 @@ doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"] trio = ["trio (>=0.16,<0.22)"] -[[package]] -name = "async-timeout" -version = "4.0.2" -description = "Timeout context manager for asyncio programs" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"}, - {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"}, -] - -[package.dependencies] -typing-extensions = {version = ">=3.6.5", markers = "python_version < \"3.8\""} - -[[package]] -name = "asynctest" -version = "0.13.0" -description = "Enhance the standard unittest package with features for testing asyncio libraries" -category = "main" -optional = false -python-versions = ">=3.5" -files = [ - {file = "asynctest-0.13.0-py3-none-any.whl", hash = "sha256:5da6118a7e6d6b54d83a8f7197769d046922a44d2a99c21382f0a6e4fadae676"}, - {file = "asynctest-0.13.0.tar.gz", hash = "sha256:c27862842d15d83e6a34eb0b2866c323880eb3a75e4485b079ea11748fd77fac"}, -] - [[package]] name = "atomicwrites" version = "1.4.1" @@ -292,46 +79,6 @@ docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib- tests = ["attrs[tests-no-zope]", "zope.interface"] tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy (>=0.971,<0.990)", "mypy (>=0.971,<0.990)", "pympler", "pympler", "pytest (>=4.3.0)", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-mypy-plugins", "pytest-xdist[psutil]", "pytest-xdist[psutil]"] -[[package]] -name = "boto3" -version = "1.21.21" -description = "The AWS SDK for Python" -category = "main" -optional = false -python-versions = ">= 3.6" -files = [ - {file = "boto3-1.21.21-py3-none-any.whl", hash = "sha256:8fa32fcc8be38327bd667237223d71e5e4b2475f39d6882aca4dbad19fff8c29"}, - {file = "boto3-1.21.21.tar.gz", hash = "sha256:6fa0622f308cfd1da758966fc98b52fbd74b80606d14586c8ad82c7a6c4f32d0"}, -] - -[package.dependencies] -botocore = ">=1.24.21,<1.25.0" -jmespath = ">=0.7.1,<2.0.0" -s3transfer = ">=0.5.0,<0.6.0" - -[package.extras] -crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] - -[[package]] -name = "botocore" -version = "1.24.21" -description = "Low-level, data-driven core of boto 3." -category = "main" -optional = false -python-versions = ">= 3.6" -files = [ - {file = "botocore-1.24.21-py3-none-any.whl", hash = "sha256:92daca8775e738a9db9b465d533019285f09d541e903233261299fd87c2f842c"}, - {file = "botocore-1.24.21.tar.gz", hash = "sha256:7e976cfd0a61601e74624ef8f5246b40a01f2cce73a011ef29cf80a6e371d0fa"}, -] - -[package.dependencies] -jmespath = ">=0.7.1,<2.0.0" -python-dateutil = ">=2.1,<3.0.0" -urllib3 = ">=1.25.4,<1.27" - -[package.extras] -crt = ["awscrt (==0.13.5)"] - [[package]] name = "certifi" version = "2022.12.7" @@ -623,90 +370,6 @@ files = [ docs = ["furo (>=2022.12.7)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] testing = ["covdefaults (>=2.2.2)", "coverage (>=7.0.1)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-timeout (>=2.1)"] -[[package]] -name = "frozenlist" -version = "1.3.3" -description = "A list-like structure which implements collections.abc.MutableSequence" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "frozenlist-1.3.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff8bf625fe85e119553b5383ba0fb6aa3d0ec2ae980295aaefa552374926b3f4"}, - {file = "frozenlist-1.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dfbac4c2dfcc082fcf8d942d1e49b6aa0766c19d3358bd86e2000bf0fa4a9cf0"}, - {file = "frozenlist-1.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b1c63e8d377d039ac769cd0926558bb7068a1f7abb0f003e3717ee003ad85530"}, - {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fdfc24dcfce5b48109867c13b4cb15e4660e7bd7661741a391f821f23dfdca7"}, - {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c926450857408e42f0bbc295e84395722ce74bae69a3b2aa2a65fe22cb14b99"}, - {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1841e200fdafc3d51f974d9d377c079a0694a8f06de2e67b48150328d66d5483"}, - {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f470c92737afa7d4c3aacc001e335062d582053d4dbe73cda126f2d7031068dd"}, - {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:783263a4eaad7c49983fe4b2e7b53fa9770c136c270d2d4bbb6d2192bf4d9caf"}, - {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:924620eef691990dfb56dc4709f280f40baee568c794b5c1885800c3ecc69816"}, - {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ae4dc05c465a08a866b7a1baf360747078b362e6a6dbeb0c57f234db0ef88ae0"}, - {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:bed331fe18f58d844d39ceb398b77d6ac0b010d571cba8267c2e7165806b00ce"}, - {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:02c9ac843e3390826a265e331105efeab489ffaf4dd86384595ee8ce6d35ae7f"}, - {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9545a33965d0d377b0bc823dcabf26980e77f1b6a7caa368a365a9497fb09420"}, - {file = "frozenlist-1.3.3-cp310-cp310-win32.whl", hash = "sha256:d5cd3ab21acbdb414bb6c31958d7b06b85eeb40f66463c264a9b343a4e238642"}, - {file = "frozenlist-1.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:b756072364347cb6aa5b60f9bc18e94b2f79632de3b0190253ad770c5df17db1"}, - {file = "frozenlist-1.3.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b4395e2f8d83fbe0c627b2b696acce67868793d7d9750e90e39592b3626691b7"}, - {file = "frozenlist-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14143ae966a6229350021384870458e4777d1eae4c28d1a7aa47f24d030e6678"}, - {file = "frozenlist-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5d8860749e813a6f65bad8285a0520607c9500caa23fea6ee407e63debcdbef6"}, - {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23d16d9f477bb55b6154654e0e74557040575d9d19fe78a161bd33d7d76808e8"}, - {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb82dbba47a8318e75f679690190c10a5e1f447fbf9df41cbc4c3afd726d88cb"}, - {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9309869032abb23d196cb4e4db574232abe8b8be1339026f489eeb34a4acfd91"}, - {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a97b4fe50b5890d36300820abd305694cb865ddb7885049587a5678215782a6b"}, - {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c188512b43542b1e91cadc3c6c915a82a5eb95929134faf7fd109f14f9892ce4"}, - {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:303e04d422e9b911a09ad499b0368dc551e8c3cd15293c99160c7f1f07b59a48"}, - {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0771aed7f596c7d73444c847a1c16288937ef988dc04fb9f7be4b2aa91db609d"}, - {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:66080ec69883597e4d026f2f71a231a1ee9887835902dbe6b6467d5a89216cf6"}, - {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:41fe21dc74ad3a779c3d73a2786bdf622ea81234bdd4faf90b8b03cad0c2c0b4"}, - {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f20380df709d91525e4bee04746ba612a4df0972c1b8f8e1e8af997e678c7b81"}, - {file = "frozenlist-1.3.3-cp311-cp311-win32.whl", hash = "sha256:f30f1928162e189091cf4d9da2eac617bfe78ef907a761614ff577ef4edfb3c8"}, - {file = "frozenlist-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:a6394d7dadd3cfe3f4b3b186e54d5d8504d44f2d58dcc89d693698e8b7132b32"}, - {file = "frozenlist-1.3.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8df3de3a9ab8325f94f646609a66cbeeede263910c5c0de0101079ad541af332"}, - {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0693c609e9742c66ba4870bcee1ad5ff35462d5ffec18710b4ac89337ff16e27"}, - {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd4210baef299717db0a600d7a3cac81d46ef0e007f88c9335db79f8979c0d3d"}, - {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:394c9c242113bfb4b9aa36e2b80a05ffa163a30691c7b5a29eba82e937895d5e"}, - {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6327eb8e419f7d9c38f333cde41b9ae348bec26d840927332f17e887a8dcb70d"}, - {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e24900aa13212e75e5b366cb9065e78bbf3893d4baab6052d1aca10d46d944c"}, - {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3843f84a6c465a36559161e6c59dce2f2ac10943040c2fd021cfb70d58c4ad56"}, - {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:84610c1502b2461255b4c9b7d5e9c48052601a8957cd0aea6ec7a7a1e1fb9420"}, - {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:c21b9aa40e08e4f63a2f92ff3748e6b6c84d717d033c7b3438dd3123ee18f70e"}, - {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:efce6ae830831ab6a22b9b4091d411698145cb9b8fc869e1397ccf4b4b6455cb"}, - {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:40de71985e9042ca00b7953c4f41eabc3dc514a2d1ff534027f091bc74416401"}, - {file = "frozenlist-1.3.3-cp37-cp37m-win32.whl", hash = "sha256:180c00c66bde6146a860cbb81b54ee0df350d2daf13ca85b275123bbf85de18a"}, - {file = "frozenlist-1.3.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9bbbcedd75acdfecf2159663b87f1bb5cfc80e7cd99f7ddd9d66eb98b14a8411"}, - {file = "frozenlist-1.3.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:034a5c08d36649591be1cbb10e09da9f531034acfe29275fc5454a3b101ce41a"}, - {file = "frozenlist-1.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba64dc2b3b7b158c6660d49cdb1d872d1d0bf4e42043ad8d5006099479a194e5"}, - {file = "frozenlist-1.3.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:47df36a9fe24054b950bbc2db630d508cca3aa27ed0566c0baf661225e52c18e"}, - {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:008a054b75d77c995ea26629ab3a0c0d7281341f2fa7e1e85fa6153ae29ae99c"}, - {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:841ea19b43d438a80b4de62ac6ab21cfe6827bb8a9dc62b896acc88eaf9cecba"}, - {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e235688f42b36be2b6b06fc37ac2126a73b75fb8d6bc66dd632aa35286238703"}, - {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca713d4af15bae6e5d79b15c10c8522859a9a89d3b361a50b817c98c2fb402a2"}, - {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ac5995f2b408017b0be26d4a1d7c61bce106ff3d9e3324374d66b5964325448"}, - {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4ae8135b11652b08a8baf07631d3ebfe65a4c87909dbef5fa0cdde440444ee4"}, - {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4ea42116ceb6bb16dbb7d526e242cb6747b08b7710d9782aa3d6732bd8d27649"}, - {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:810860bb4bdce7557bc0febb84bbd88198b9dbc2022d8eebe5b3590b2ad6c842"}, - {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:ee78feb9d293c323b59a6f2dd441b63339a30edf35abcb51187d2fc26e696d13"}, - {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0af2e7c87d35b38732e810befb9d797a99279cbb85374d42ea61c1e9d23094b3"}, - {file = "frozenlist-1.3.3-cp38-cp38-win32.whl", hash = "sha256:899c5e1928eec13fd6f6d8dc51be23f0d09c5281e40d9cf4273d188d9feeaf9b"}, - {file = "frozenlist-1.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:7f44e24fa70f6fbc74aeec3e971f60a14dde85da364aa87f15d1be94ae75aeef"}, - {file = "frozenlist-1.3.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2b07ae0c1edaa0a36339ec6cce700f51b14a3fc6545fdd32930d2c83917332cf"}, - {file = "frozenlist-1.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ebb86518203e12e96af765ee89034a1dbb0c3c65052d1b0c19bbbd6af8a145e1"}, - {file = "frozenlist-1.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5cf820485f1b4c91e0417ea0afd41ce5cf5965011b3c22c400f6d144296ccbc0"}, - {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c11e43016b9024240212d2a65043b70ed8dfd3b52678a1271972702d990ac6d"}, - {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8fa3c6e3305aa1146b59a09b32b2e04074945ffcfb2f0931836d103a2c38f936"}, - {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:352bd4c8c72d508778cf05ab491f6ef36149f4d0cb3c56b1b4302852255d05d5"}, - {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65a5e4d3aa679610ac6e3569e865425b23b372277f89b5ef06cf2cdaf1ebf22b"}, - {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e2c1185858d7e10ff045c496bbf90ae752c28b365fef2c09cf0fa309291669"}, - {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f163d2fd041c630fed01bc48d28c3ed4a3b003c00acd396900e11ee5316b56bb"}, - {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:05cdb16d09a0832eedf770cb7bd1fe57d8cf4eaf5aced29c4e41e3f20b30a784"}, - {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:8bae29d60768bfa8fb92244b74502b18fae55a80eac13c88eb0b496d4268fd2d"}, - {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:eedab4c310c0299961ac285591acd53dc6723a1ebd90a57207c71f6e0c2153ab"}, - {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3bbdf44855ed8f0fbcd102ef05ec3012d6a4fd7c7562403f76ce6a52aeffb2b1"}, - {file = "frozenlist-1.3.3-cp39-cp39-win32.whl", hash = "sha256:efa568b885bca461f7c7b9e032655c0c143d305bf01c30caf6db2854a4532b38"}, - {file = "frozenlist-1.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:cfe33efc9cb900a4c46f91a5ceba26d6df370ffddd9ca386eb1d4f0ad97b9ea9"}, - {file = "frozenlist-1.3.3.tar.gz", hash = "sha256:58bcc55721e8a90b88332d6cd441261ebb22342e238296bb330968952fbb3a6a"}, -] - [[package]] name = "future" version = "0.18.3" @@ -835,18 +498,6 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] -[[package]] -name = "jmespath" -version = "1.0.1" -description = "JSON Matching Expressions" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, - {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, -] - [[package]] name = "macholib" version = "1.16.2" @@ -862,106 +513,6 @@ files = [ [package.dependencies] altgraph = ">=0.17" -[[package]] -name = "minio" -version = "7.1.8" -description = "MinIO Python SDK for Amazon S3 Compatible Cloud Storage" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "minio-7.1.8-py3-none-any.whl", hash = "sha256:0feadaf4cfd8608ccaf17b092c799bbe4b9e0692f9c15f5e03c5ee21d85e8cdb"}, - {file = "minio-7.1.8.tar.gz", hash = "sha256:c3fe5448ca281c88fe58f0486a73e0df6cdb05e8dbf72eb79e570e71125c1686"}, -] - -[package.dependencies] -certifi = "*" -urllib3 = "*" - -[[package]] -name = "multidict" -version = "6.0.4" -description = "multidict implementation" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"}, - {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"}, - {file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"}, - {file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"}, - {file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"}, - {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"}, - {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"}, - {file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"}, - {file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"}, - {file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"}, - {file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"}, - {file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"}, - {file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"}, - {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"}, - {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"}, - {file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"}, - {file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"}, - {file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"}, - {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"}, - {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"}, - {file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"}, - {file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"}, - {file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"}, - {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, -] - [[package]] name = "nodeenv" version = "1.7.0" @@ -1003,27 +554,6 @@ files = [ [package.dependencies] future = "*" -[[package]] -name = "pilot-platform-common" -version = "0.1.3" -description = "Generates entity ID and connects with Vault (secret engine) to retrieve credentials" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pilot-platform-common-0.1.3.tar.gz", hash = "sha256:c9b3adbe9654d1c8291a74e9ee742dcca3cd0e598b8d0146a05888950864850d"}, - {file = "pilot_platform_common-0.1.3-py3-none-any.whl", hash = "sha256:1873f3fa062ec9a34910bc5a2f9ead8020a1188a2567d4cc1c850c2206164e7b"}, -] - -[package.dependencies] -aioboto3 = "9.6.0" -aioredis = ">=2.0.0,<3.0.0" -httpx = "0.23.0" -minio = "7.1.8" -python-dotenv = "0.19.1" -python-json-logger = "2.0.2" -xmltodict = "0.13.0" - [[package]] name = "platformdirs" version = "2.6.2" @@ -1123,56 +653,153 @@ files = [ [[package]] name = "pydantic" -version = "1.10.4" -description = "Data validation and settings management using python type hints" +version = "2.0.3" +description = "Data validation using Python type hints" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic-1.10.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5635de53e6686fe7a44b5cf25fcc419a0d5e5c1a1efe73d49d48fe7586db854"}, - {file = "pydantic-1.10.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6dc1cc241440ed7ca9ab59d9929075445da6b7c94ced281b3dd4cfe6c8cff817"}, - {file = "pydantic-1.10.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51bdeb10d2db0f288e71d49c9cefa609bca271720ecd0c58009bd7504a0c464c"}, - {file = "pydantic-1.10.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78cec42b95dbb500a1f7120bdf95c401f6abb616bbe8785ef09887306792e66e"}, - {file = "pydantic-1.10.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8775d4ef5e7299a2f4699501077a0defdaac5b6c4321173bcb0f3c496fbadf85"}, - {file = "pydantic-1.10.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:572066051eeac73d23f95ba9a71349c42a3e05999d0ee1572b7860235b850cc6"}, - {file = "pydantic-1.10.4-cp310-cp310-win_amd64.whl", hash = "sha256:7feb6a2d401f4d6863050f58325b8d99c1e56f4512d98b11ac64ad1751dc647d"}, - {file = "pydantic-1.10.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:39f4a73e5342b25c2959529f07f026ef58147249f9b7431e1ba8414a36761f53"}, - {file = "pydantic-1.10.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:983e720704431a6573d626b00662eb78a07148c9115129f9b4351091ec95ecc3"}, - {file = "pydantic-1.10.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75d52162fe6b2b55964fbb0af2ee58e99791a3138588c482572bb6087953113a"}, - {file = "pydantic-1.10.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fdf8d759ef326962b4678d89e275ffc55b7ce59d917d9f72233762061fd04a2d"}, - {file = "pydantic-1.10.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:05a81b006be15655b2a1bae5faa4280cf7c81d0e09fcb49b342ebf826abe5a72"}, - {file = "pydantic-1.10.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d88c4c0e5c5dfd05092a4b271282ef0588e5f4aaf345778056fc5259ba098857"}, - {file = "pydantic-1.10.4-cp311-cp311-win_amd64.whl", hash = "sha256:6a05a9db1ef5be0fe63e988f9617ca2551013f55000289c671f71ec16f4985e3"}, - {file = "pydantic-1.10.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:887ca463c3bc47103c123bc06919c86720e80e1214aab79e9b779cda0ff92a00"}, - {file = "pydantic-1.10.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdf88ab63c3ee282c76d652fc86518aacb737ff35796023fae56a65ced1a5978"}, - {file = "pydantic-1.10.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a48f1953c4a1d9bd0b5167ac50da9a79f6072c63c4cef4cf2a3736994903583e"}, - {file = "pydantic-1.10.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a9f2de23bec87ff306aef658384b02aa7c32389766af3c5dee9ce33e80222dfa"}, - {file = "pydantic-1.10.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:cd8702c5142afda03dc2b1ee6bc358b62b3735b2cce53fc77b31ca9f728e4bc8"}, - {file = "pydantic-1.10.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6e7124d6855b2780611d9f5e1e145e86667eaa3bd9459192c8dc1a097f5e9903"}, - {file = "pydantic-1.10.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b53e1d41e97063d51a02821b80538053ee4608b9a181c1005441f1673c55423"}, - {file = "pydantic-1.10.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:55b1625899acd33229c4352ce0ae54038529b412bd51c4915349b49ca575258f"}, - {file = "pydantic-1.10.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:301d626a59edbe5dfb48fcae245896379a450d04baeed50ef40d8199f2733b06"}, - {file = "pydantic-1.10.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6f9d649892a6f54a39ed56b8dfd5e08b5f3be5f893da430bed76975f3735d15"}, - {file = "pydantic-1.10.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d7b5a3821225f5c43496c324b0d6875fde910a1c2933d726a743ce328fbb2a8c"}, - {file = "pydantic-1.10.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f2f7eb6273dd12472d7f218e1fef6f7c7c2f00ac2e1ecde4db8824c457300416"}, - {file = "pydantic-1.10.4-cp38-cp38-win_amd64.whl", hash = "sha256:4b05697738e7d2040696b0a66d9f0a10bec0efa1883ca75ee9e55baf511909d6"}, - {file = "pydantic-1.10.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a9a6747cac06c2beb466064dda999a13176b23535e4c496c9d48e6406f92d42d"}, - {file = "pydantic-1.10.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eb992a1ef739cc7b543576337bebfc62c0e6567434e522e97291b251a41dad7f"}, - {file = "pydantic-1.10.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:990406d226dea0e8f25f643b370224771878142155b879784ce89f633541a024"}, - {file = "pydantic-1.10.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e82a6d37a95e0b1b42b82ab340ada3963aea1317fd7f888bb6b9dfbf4fff57c"}, - {file = "pydantic-1.10.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9193d4f4ee8feca58bc56c8306bcb820f5c7905fd919e0750acdeeeef0615b28"}, - {file = "pydantic-1.10.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2b3ce5f16deb45c472dde1a0ee05619298c864a20cded09c4edd820e1454129f"}, - {file = "pydantic-1.10.4-cp39-cp39-win_amd64.whl", hash = "sha256:9cbdc268a62d9a98c56e2452d6c41c0263d64a2009aac69246486f01b4f594c4"}, - {file = "pydantic-1.10.4-py3-none-any.whl", hash = "sha256:4948f264678c703f3877d1c8877c4e3b2e12e549c57795107f08cf70c6ec7774"}, - {file = "pydantic-1.10.4.tar.gz", hash = "sha256:b9a3859f24eb4e097502a3be1fb4b2abb79b6103dd9e2e0edb70613a4459a648"}, + {file = "pydantic-2.0.3-py3-none-any.whl", hash = "sha256:614eb3321eb600c81899a88fa9858b008e3c79e0d4f1b49ab1f516b4b0c27cfb"}, + {file = "pydantic-2.0.3.tar.gz", hash = "sha256:94f13e0dcf139a5125e88283fc999788d894e14ed90cf478bcc2ee50bd4fc630"}, ] [package.dependencies] -typing-extensions = ">=4.2.0" +annotated-types = ">=0.4.0" +pydantic-core = "2.3.0" +typing-extensions = ">=4.6.1" [package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.3)"] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.3.0" +description = "" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic_core-2.3.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:4542c98b8364b976593703a2dda97377433b102f380b61bc3a2cbc2fbdae1d1f"}, + {file = "pydantic_core-2.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9342de50824b40f55d2600f66c6f9a91a3a24851eca39145a749a3dc804ee599"}, + {file = "pydantic_core-2.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:539432f911686cb80284c30b33eaf9f4fd9a11e1111fe0dc98fdbdce69b49821"}, + {file = "pydantic_core-2.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38a0e7ee65c8999394d92d9c724434cb629279d19844f2b69d9bbc46dc8b8b61"}, + {file = "pydantic_core-2.3.0-cp310-cp310-manylinux_2_24_armv7l.whl", hash = "sha256:e3ed6834cc005798187a56c248a2240207cb8ffdda1c89e9afda4c3d526c2ea0"}, + {file = "pydantic_core-2.3.0-cp310-cp310-manylinux_2_24_ppc64le.whl", hash = "sha256:e72ac299a6bf732a60852d052acf3999d234686755a02ba111e85e7ebf8155b1"}, + {file = "pydantic_core-2.3.0-cp310-cp310-manylinux_2_24_s390x.whl", hash = "sha256:616b3451b05ca63b8f433c627f68046b39543faeaa4e50d8c6699a2a1e4b85a5"}, + {file = "pydantic_core-2.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:adcb9c8848e15c613e483e0b99767ae325af27fe0dbd866df01fe5849d06e6e1"}, + {file = "pydantic_core-2.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:464bf799b422be662e5e562e62beeffc9eaa907d381a9d63a2556615bbda286d"}, + {file = "pydantic_core-2.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4638ebc17de08c2f3acba557efeb6f195c88b7299d8c55c0bb4e20638bbd4d03"}, + {file = "pydantic_core-2.3.0-cp310-none-win32.whl", hash = "sha256:9ff322c7e1030543d35d83bb521b69114d3d150750528d7757544f639def9ad6"}, + {file = "pydantic_core-2.3.0-cp310-none-win_amd64.whl", hash = "sha256:4824eb018f0a4680b1e434697a9bf3f41c7799b80076d06530cbbd212e040ccc"}, + {file = "pydantic_core-2.3.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:0aa429578e23885b3984c49d687cd05ab06f0b908ea1711a8bf7e503b7f97160"}, + {file = "pydantic_core-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:20d710c1f79af930b8891bcebd84096798e4387ab64023ef41521d58f21277d3"}, + {file = "pydantic_core-2.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:309f45d4d7481d6f09cb9e35c72caa0e50add4a30bb08c04c5fe5956a0158633"}, + {file = "pydantic_core-2.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bcfb7be905aa849bd882262e1df3f75b564e2f708b4b4c7ad2d3deaf5410562"}, + {file = "pydantic_core-2.3.0-cp311-cp311-manylinux_2_24_armv7l.whl", hash = "sha256:85cd9c0af34e371390e3cb2f3a470b0b40cc07568c1e966c638c49062be6352d"}, + {file = "pydantic_core-2.3.0-cp311-cp311-manylinux_2_24_ppc64le.whl", hash = "sha256:37c5028cebdf731298724070838fb3a71ef1fbd201d193d311ac2cbdbca25a23"}, + {file = "pydantic_core-2.3.0-cp311-cp311-manylinux_2_24_s390x.whl", hash = "sha256:e4208f23f12d0ad206a07a489ef4cb15722c10b62774c4460ee4123250be938e"}, + {file = "pydantic_core-2.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c24465dd11b65c8510f251b095fc788c7c91481c81840112fe3f76c30793a455"}, + {file = "pydantic_core-2.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3cd7ee8bbfab277ab56e272221886fd33a1b5943fbf45ae9195aa6a48715a8a0"}, + {file = "pydantic_core-2.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0fc7e0b056b66cc536e97ef60f48b3b289f6b3b62ac225afd4b22a42434617bf"}, + {file = "pydantic_core-2.3.0-cp311-none-win32.whl", hash = "sha256:4788135db4bd83a5edc3522b11544b013be7d25b74b155e08dd3b20cd6663bbb"}, + {file = "pydantic_core-2.3.0-cp311-none-win_amd64.whl", hash = "sha256:f93c867e5e85584a28c6a6feb6f2086d717266eb5d1210d096dd717b7f4dec04"}, + {file = "pydantic_core-2.3.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:73f62bb7fd862d9bcd886e10612bade6fe042eda8b47e8c129892bcfb7b45e84"}, + {file = "pydantic_core-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4d889d498fce64bfcd8adf1a78579a7f626f825cbeb2956a24a29b35f9a1df32"}, + {file = "pydantic_core-2.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d55e38a89ec2ae17b2fa7ffeda6b70f63afab1888bd0d57aaa7b7879760acb4"}, + {file = "pydantic_core-2.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1aefebb506bc1fe355d91d25f12bcdea7f4d7c2d9f0f6716dd025543777c99a5"}, + {file = "pydantic_core-2.3.0-cp312-cp312-manylinux_2_24_armv7l.whl", hash = "sha256:6441a29f42585f085db0c04cd0557d4cbbb46fa68a0972409b1cfe9f430280c1"}, + {file = "pydantic_core-2.3.0-cp312-cp312-manylinux_2_24_ppc64le.whl", hash = "sha256:47e8f034be31390a8f525431eb5e803a78ce7e2e11b32abf5361a972e14e6b61"}, + {file = "pydantic_core-2.3.0-cp312-cp312-manylinux_2_24_s390x.whl", hash = "sha256:ad814864aba263be9c83ada44a95f72d10caabbf91589321f95c29c902bdcff0"}, + {file = "pydantic_core-2.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9eff3837d447fccf2ac38c259b14ab9cbde700df355a45a1f3ff244d5e78f8b6"}, + {file = "pydantic_core-2.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:534f3f63c000f08050c6f7f4378bf2b52d7ba9214e9d35e3f60f7ad24a4d6425"}, + {file = "pydantic_core-2.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ef6a222d54f742c24f6b143aab088702db3a827b224e75b9dd28b38597c595fe"}, + {file = "pydantic_core-2.3.0-cp312-none-win32.whl", hash = "sha256:4e26944e64ecc1d7b19db954c0f7b471f3b141ec8e1a9f57cfe27671525cd248"}, + {file = "pydantic_core-2.3.0-cp312-none-win_amd64.whl", hash = "sha256:019c5c41941438570dfc7d3f0ae389b2425add1775a357ce1e83ed1434f943d6"}, + {file = "pydantic_core-2.3.0-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:27c1bbfb9d84a75cf33b7f19b53c29eb7ead99b235fce52aced5507174ab8f98"}, + {file = "pydantic_core-2.3.0-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:7cb496e934b71f1ade844ab91d6ccac78a3520e5df02fdb2357f85a71e541e69"}, + {file = "pydantic_core-2.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5af2d43b1978958d91351afbcc9b4d0cfe144c46c61740e82aaac8bb39ab1a4d"}, + {file = "pydantic_core-2.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d3097c39d7d4e8dba2ef86de171dcccad876c36d8379415ba18a5a4d0533510"}, + {file = "pydantic_core-2.3.0-cp37-cp37m-manylinux_2_24_armv7l.whl", hash = "sha256:dd3b023f3317dbbbc775e43651ce1a31a9cea46216ad0b5be37afc18a2007699"}, + {file = "pydantic_core-2.3.0-cp37-cp37m-manylinux_2_24_ppc64le.whl", hash = "sha256:27babb9879bf2c45ed655d02639f4c30e2b9ef1b71ce59c2305bbf7287910a18"}, + {file = "pydantic_core-2.3.0-cp37-cp37m-manylinux_2_24_s390x.whl", hash = "sha256:2183a9e18cdc0de53bdaa1675f237259162abeb62d6ac9e527c359c1074dc55d"}, + {file = "pydantic_core-2.3.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c089d8e7f1b4db08b2f8e4107304eec338df046275dad432635a9be9531e2fc8"}, + {file = "pydantic_core-2.3.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:2f10aa5452b865818dd0137f568d443f5e93b60a27080a01aa4b7512c7ba13a3"}, + {file = "pydantic_core-2.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:f642313d559f9d9a00c4de6820124059cc3342a0d0127b18301de2c680d5ea40"}, + {file = "pydantic_core-2.3.0-cp37-none-win32.whl", hash = "sha256:45327fc57afbe3f2c3d7f54a335d5cecee8a9fdb3906a2fbed8af4092f4926df"}, + {file = "pydantic_core-2.3.0-cp37-none-win_amd64.whl", hash = "sha256:e427b66596a6441a5607dfc0085b47d36073f88da7ac48afd284263b9b99e6ce"}, + {file = "pydantic_core-2.3.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:0b3d781c71b8bfb621ef23b9c874933e2cd33237c1a65cc20eeb37437f8e7e18"}, + {file = "pydantic_core-2.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ad46027dbd5c1db87dc0b49becbe23093b143a20302028d387dae37ee5ef95f5"}, + {file = "pydantic_core-2.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39aa09ed7ce2a648c904f79032d16dda29e6913112af8465a7bf710eef23c7ca"}, + {file = "pydantic_core-2.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05b4bf8c58409586a7a04c858a86ab10f28c6c1a7c33da65e0326c59d5b0ab16"}, + {file = "pydantic_core-2.3.0-cp38-cp38-manylinux_2_24_armv7l.whl", hash = "sha256:ba2b807d2b62c446120906b8580cddae1d76d3de4efbb95ccc87f5e35c75b4b2"}, + {file = "pydantic_core-2.3.0-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:ea955e4ed21f4bbb9b83fea09fc6af0bed82e69ecf6b35ec89237a0a49633033"}, + {file = "pydantic_core-2.3.0-cp38-cp38-manylinux_2_24_s390x.whl", hash = "sha256:06884c07956526ac9ebfef40fe21a11605569b8fc0e2054a375fb39c978bf48f"}, + {file = "pydantic_core-2.3.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f868e731a18b403b88aa434d960489ceeed0ddeb44ebc02389540731a67705e0"}, + {file = "pydantic_core-2.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cb08fab0fc1db15c277b72e33ac74ad9c0c789413da8984a3eacb22a94b42ef4"}, + {file = "pydantic_core-2.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6ca34c29fbd6592de5fd39e80c1993634d704c4e7e14ba54c87b2c7c53da68fe"}, + {file = "pydantic_core-2.3.0-cp38-none-win32.whl", hash = "sha256:cd782807d35c8a41aaa7d30b5107784420eefd9fdc1c760d86007d43ae00b15d"}, + {file = "pydantic_core-2.3.0-cp38-none-win_amd64.whl", hash = "sha256:01f56d5ee70b1d39c0fd08372cc5142274070ab7181d17c86035f130eebc05b8"}, + {file = "pydantic_core-2.3.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:78b1ac0151271ce62bc2b33755f1043eda6a310373143a2f27e2bcd3d5fc8633"}, + {file = "pydantic_core-2.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:64bfd2c35a2c350f73ac52dc134d8775f93359c4c969280a6fe5301b5b6e7431"}, + {file = "pydantic_core-2.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:937c0fe9538f1212b62df6a68f8d78df3572fe3682d9a0dd8851eac8a4e46063"}, + {file = "pydantic_core-2.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d965c7c4b40d1cedec9188782e98bd576f9a04868835604200c3a6e817b824f"}, + {file = "pydantic_core-2.3.0-cp39-cp39-manylinux_2_24_armv7l.whl", hash = "sha256:ad442b8585ed4a3c2d22e4bf7b465d9b7d281e055b09719a8aeb5b576422dc9b"}, + {file = "pydantic_core-2.3.0-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:4bf20c9722821fce766e685718e739deeccc60d6bc7be5029281db41f999ee0c"}, + {file = "pydantic_core-2.3.0-cp39-cp39-manylinux_2_24_s390x.whl", hash = "sha256:f3dd5333049b5b3faa739e0f40b77cc8b7a1aded2f2da0e28794c81586d7b08a"}, + {file = "pydantic_core-2.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dc5f516b24d24bc9e8dd9305460899f38302b3c4f9752663b396ef9848557bf"}, + {file = "pydantic_core-2.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:055f7ea6b1fbb37880d66d70eefd22dd319b09c79d2cb99b1dbfeb34b653b0b2"}, + {file = "pydantic_core-2.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:af693a89db6d6ac97dd84dd7769b3f2bd9007b578127d0e7dda03053f4d3b34b"}, + {file = "pydantic_core-2.3.0-cp39-none-win32.whl", hash = "sha256:f60e31e3e15e8c294bf70c60f8ae4d0c3caf3af8f26466e9aa8ea4c01302749b"}, + {file = "pydantic_core-2.3.0-cp39-none-win_amd64.whl", hash = "sha256:2b79f3681481f4424d7845cc7a261d5a4baa810d656b631fa844dc9967b36a7b"}, + {file = "pydantic_core-2.3.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:a666134b41712e30a71afaa26deeb4da374179f769fa49784cdf0e7698880fab"}, + {file = "pydantic_core-2.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c119e9227487ad3d7c3c737d896afe548a6be554091f9745da1f4b489c40561"}, + {file = "pydantic_core-2.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73929a2fb600a2333fce2efd92596cff5e6bf8946e20e93c067b220760064862"}, + {file = "pydantic_core-2.3.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:41bbc2678a5b6a19371b2cb51f30ccea71f0c14b26477d2d884fed761cea42c7"}, + {file = "pydantic_core-2.3.0-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dcbff997f47d45bf028bda4c3036bb3101e89a3df271281d392b6175f71c71d1"}, + {file = "pydantic_core-2.3.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:afa8808159169368b66e4fbeafac6c6fd8f26246dc4d0dcc2caf94bd9cf1b828"}, + {file = "pydantic_core-2.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:12be3b5f54f8111ca38e6b7277f26c23ba5cb3344fae06f879a0a93dfc8b479e"}, + {file = "pydantic_core-2.3.0-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ed5babdcd3d052ba5cf8832561f18df20778c7ccf12587b2d82f7bf3bf259a0e"}, + {file = "pydantic_core-2.3.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d642e5c029e2acfacf6aa0a7a3e822086b3b777c70d364742561f9ca64c1ffc"}, + {file = "pydantic_core-2.3.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ba3073eb38a1294e8c7902989fb80a7a147a69db2396818722bd078476586a0"}, + {file = "pydantic_core-2.3.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5146a6749b1905e04e62e0ad4622f079e5582f8b3abef5fb64516c623127908"}, + {file = "pydantic_core-2.3.0-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:deeb64335f489c3c11949cbd1d1668b3f1fb2d1c6a5bf40e126ef7bf95f9fa40"}, + {file = "pydantic_core-2.3.0-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:31acc37288b8e69e4849f618c3d5cf13b58077c1a1ff9ade0b3065ba974cd385"}, + {file = "pydantic_core-2.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:e09d9f6d722de9d4c1c5f122ea9bc6b25a05f975457805af4dcab7b0128aacbf"}, + {file = "pydantic_core-2.3.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ba6a8cf089222a171b8f84e6ec2d10f7a9d14f26be3a347b14775a8741810676"}, + {file = "pydantic_core-2.3.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1fd1b24e9bcddcb168437686677104e205c8e25b066e73ffdf331d3bb8792b"}, + {file = "pydantic_core-2.3.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eda1a89c4526826c0a87d33596a4cd15b8f58e9250f503e39af1699ba9c878e8"}, + {file = "pydantic_core-2.3.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3e9a18401a28db4358da2e191508702dbf065f2664c710708cdf9552b9fa50c"}, + {file = "pydantic_core-2.3.0-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:a439fd0d45d51245bbde799726adda5bd18aed3fa2b01ab2e6a64d6d13776fa3"}, + {file = "pydantic_core-2.3.0-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:bf6a1d2c920cc9528e884850a4b2ee7629e3d362d5c44c66526d4097bbb07a1a"}, + {file = "pydantic_core-2.3.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e33fcbea3b63a339dd94de0fc442fefacfe681cc7027ce63f67af9f7ceec7422"}, + {file = "pydantic_core-2.3.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:bf3ed993bdf4754909f175ff348cf8f78d4451215b8aa338633f149ca3b1f37a"}, + {file = "pydantic_core-2.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7584171eb3115acd4aba699bc836634783f5bd5aab131e88d8eeb8a3328a4a72"}, + {file = "pydantic_core-2.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1624baa76d1740711b2048f302ae9a6d73d277c55a8c3e88b53b773ebf73a971"}, + {file = "pydantic_core-2.3.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:06f33f695527f5a86e090f208978f9fd252c9cfc7e869d3b679bd71f7cb2c1fa"}, + {file = "pydantic_core-2.3.0-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7ecf0a67b212900e92f328181fed02840d74ed39553cdb38d27314e2b9c89dfa"}, + {file = "pydantic_core-2.3.0-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:45fa1e8ad6f4367ad73674ca560da8e827cc890eaf371f3ee063d6d7366a207b"}, + {file = "pydantic_core-2.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8d0dbcc57839831ae79fd24b1b83d42bc9448d79feaf3ed3fb5cbf94ffbf3eb7"}, + {file = "pydantic_core-2.3.0.tar.gz", hash = "sha256:5cfb5ac4e82c47d5dc25b209dd4c3989e284b80109f9e08b33c895080c424b4f"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pydantic-settings" +version = "2.0.1" +description = "Settings management using Pydantic" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic_settings-2.0.1-py3-none-any.whl", hash = "sha256:579bbcbec3501e62bab73867b097ae10218201950e897463c98a182ffe7ed104"}, + {file = "pydantic_settings-2.0.1.tar.gz", hash = "sha256:f440ec7cfb6dc63f03226c47b0e7803750d1b66a49ed944ac23eb4f0c84f8722"}, +] + +[package.dependencies] +pydantic = ">=2.0.1" +python-dotenv = ">=0.21.0" [[package]] name = "pyinstaller" @@ -1348,11 +975,26 @@ pytest = ">=5.0" [package.extras] dev = ["pre-commit", "pytest-asyncio", "tox"] +[[package]] +name = "pytest-random-order" +version = "1.1.0" +description = "Randomise the order in which pytest tests are run with some control over the randomness" +category = "dev" +optional = false +python-versions = ">=3.5.0" +files = [ + {file = "pytest-random-order-1.1.0.tar.gz", hash = "sha256:dbe6debb9353a7af984cc9eddbeb3577dd4dbbcc1529a79e3d21f68ed9b45605"}, + {file = "pytest_random_order-1.1.0-py3-none-any.whl", hash = "sha256:6cb1e59ab0f798bb0c3488c11ae0c70d7d3340306a466d28b28ccd8ef8c20b7e"}, +] + +[package.dependencies] +pytest = ">=3.0.0" + [[package]] name = "python-dateutil" version = "2.8.2" description = "Extensions to the standard Python datetime module" -category = "main" +category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ @@ -1365,31 +1007,19 @@ six = ">=1.5" [[package]] name = "python-dotenv" -version = "0.19.1" +version = "0.21.1" description = "Read key-value pairs from a .env file and set them as environment variables" category = "main" optional = false -python-versions = ">=3.5" +python-versions = ">=3.7" files = [ - {file = "python-dotenv-0.19.1.tar.gz", hash = "sha256:14f8185cc8d494662683e6914addcb7e95374771e707601dfc70166946b4c4b8"}, - {file = "python_dotenv-0.19.1-py2.py3-none-any.whl", hash = "sha256:bbd3da593fc49c249397cbfbcc449cf36cb02e75afc8157fcc6a81df6fb7750a"}, + {file = "python-dotenv-0.21.1.tar.gz", hash = "sha256:1c93de8f636cde3ce377292818d0e440b6e45a82f215c3744979151fa8151c49"}, + {file = "python_dotenv-0.21.1-py3-none-any.whl", hash = "sha256:41e12e0318bebc859fcc4d97d4db8d20ad21721a6aa5047dd59f090391cb549a"}, ] [package.extras] cli = ["click (>=5.0)"] -[[package]] -name = "python-json-logger" -version = "2.0.2" -description = "A python library adding a json log formatter" -category = "main" -optional = false -python-versions = ">=3.5" -files = [ - {file = "python-json-logger-2.0.2.tar.gz", hash = "sha256:202a4f29901a4b8002a6d1b958407eeb2dd1d83c18b18b816f5b64476dde9096"}, - {file = "python_json_logger-2.0.2-py3-none-any.whl", hash = "sha256:99310d148f054e858cd5f4258794ed6777e7ad2c3fd7e1c1b527f1cba4d08420"}, -] - [[package]] name = "pywin32-ctypes" version = "0.2.0" @@ -1554,24 +1184,6 @@ idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} [package.extras] idna2008 = ["idna"] -[[package]] -name = "s3transfer" -version = "0.5.2" -description = "An Amazon S3 Transfer Manager" -category = "main" -optional = false -python-versions = ">= 3.6" -files = [ - {file = "s3transfer-0.5.2-py3-none-any.whl", hash = "sha256:7a6f4c4d1fdb9a2b640244008e142cbc2cd3ae34b386584ef044dd0f27101971"}, - {file = "s3transfer-0.5.2.tar.gz", hash = "sha256:95c58c194ce657a5f4fb0b9e60a84968c808888aed628cd98ab8771fe1db98ed"}, -] - -[package.dependencies] -botocore = ">=1.12.36,<2.0a.0" - -[package.extras] -crt = ["botocore[crt] (>=1.20.29,<2.0a.0)"] - [[package]] name = "setuptools" version = "67.6.1" @@ -1593,7 +1205,7 @@ testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs ( name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "main" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1655,14 +1267,14 @@ telegram = ["requests"] [[package]] name = "typing-extensions" -version = "4.4.0" +version = "4.7.1" description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, - {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, + {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, + {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, ] [[package]] @@ -1716,181 +1328,6 @@ files = [ {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"}, ] -[[package]] -name = "wrapt" -version = "1.14.1" -description = "Module for decorators, wrappers and monkey patching." -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -files = [ - {file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"}, - {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef"}, - {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28"}, - {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59"}, - {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87"}, - {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1"}, - {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b"}, - {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462"}, - {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1"}, - {file = "wrapt-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320"}, - {file = "wrapt-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2"}, - {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4"}, - {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069"}, - {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310"}, - {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f"}, - {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656"}, - {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c"}, - {file = "wrapt-1.14.1-cp310-cp310-win32.whl", hash = "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8"}, - {file = "wrapt-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164"}, - {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907"}, - {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3"}, - {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3"}, - {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d"}, - {file = "wrapt-1.14.1-cp35-cp35m-win32.whl", hash = "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7"}, - {file = "wrapt-1.14.1-cp35-cp35m-win_amd64.whl", hash = "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00"}, - {file = "wrapt-1.14.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4"}, - {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1"}, - {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1"}, - {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff"}, - {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d"}, - {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1"}, - {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569"}, - {file = "wrapt-1.14.1-cp36-cp36m-win32.whl", hash = "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed"}, - {file = "wrapt-1.14.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471"}, - {file = "wrapt-1.14.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248"}, - {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68"}, - {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d"}, - {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77"}, - {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7"}, - {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015"}, - {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a"}, - {file = "wrapt-1.14.1-cp37-cp37m-win32.whl", hash = "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853"}, - {file = "wrapt-1.14.1-cp37-cp37m-win_amd64.whl", hash = "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c"}, - {file = "wrapt-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456"}, - {file = "wrapt-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f"}, - {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc"}, - {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1"}, - {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af"}, - {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b"}, - {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0"}, - {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57"}, - {file = "wrapt-1.14.1-cp38-cp38-win32.whl", hash = "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5"}, - {file = "wrapt-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d"}, - {file = "wrapt-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383"}, - {file = "wrapt-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7"}, - {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86"}, - {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735"}, - {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b"}, - {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3"}, - {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3"}, - {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe"}, - {file = "wrapt-1.14.1-cp39-cp39-win32.whl", hash = "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5"}, - {file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"}, - {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"}, -] - -[[package]] -name = "xmltodict" -version = "0.13.0" -description = "Makes working with XML feel like you are working with JSON" -category = "main" -optional = false -python-versions = ">=3.4" -files = [ - {file = "xmltodict-0.13.0-py2.py3-none-any.whl", hash = "sha256:aa89e8fd76320154a40d19a0df04a4695fb9dc5ba977cbb68ab3e4eb225e7852"}, - {file = "xmltodict-0.13.0.tar.gz", hash = "sha256:341595a488e3e01a85a9d8911d8912fd922ede5fecc4dce437eb4b6c8d037e56"}, -] - -[[package]] -name = "yarl" -version = "1.8.2" -description = "Yet another URL library" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "yarl-1.8.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:bb81f753c815f6b8e2ddd2eef3c855cf7da193b82396ac013c661aaa6cc6b0a5"}, - {file = "yarl-1.8.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:47d49ac96156f0928f002e2424299b2c91d9db73e08c4cd6742923a086f1c863"}, - {file = "yarl-1.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3fc056e35fa6fba63248d93ff6e672c096f95f7836938241ebc8260e062832fe"}, - {file = "yarl-1.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58a3c13d1c3005dbbac5c9f0d3210b60220a65a999b1833aa46bd6677c69b08e"}, - {file = "yarl-1.8.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10b08293cda921157f1e7c2790999d903b3fd28cd5c208cf8826b3b508026996"}, - {file = "yarl-1.8.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de986979bbd87272fe557e0a8fcb66fd40ae2ddfe28a8b1ce4eae22681728fef"}, - {file = "yarl-1.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c4fcfa71e2c6a3cb568cf81aadc12768b9995323186a10827beccf5fa23d4f8"}, - {file = "yarl-1.8.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae4d7ff1049f36accde9e1ef7301912a751e5bae0a9d142459646114c70ecba6"}, - {file = "yarl-1.8.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bf071f797aec5b96abfc735ab97da9fd8f8768b43ce2abd85356a3127909d146"}, - {file = "yarl-1.8.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:74dece2bfc60f0f70907c34b857ee98f2c6dd0f75185db133770cd67300d505f"}, - {file = "yarl-1.8.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:df60a94d332158b444301c7f569659c926168e4d4aad2cfbf4bce0e8fb8be826"}, - {file = "yarl-1.8.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:63243b21c6e28ec2375f932a10ce7eda65139b5b854c0f6b82ed945ba526bff3"}, - {file = "yarl-1.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cfa2bbca929aa742b5084fd4663dd4b87c191c844326fcb21c3afd2d11497f80"}, - {file = "yarl-1.8.2-cp310-cp310-win32.whl", hash = "sha256:b05df9ea7496df11b710081bd90ecc3a3db6adb4fee36f6a411e7bc91a18aa42"}, - {file = "yarl-1.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:24ad1d10c9db1953291f56b5fe76203977f1ed05f82d09ec97acb623a7976574"}, - {file = "yarl-1.8.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2a1fca9588f360036242f379bfea2b8b44cae2721859b1c56d033adfd5893634"}, - {file = "yarl-1.8.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f37db05c6051eff17bc832914fe46869f8849de5b92dc4a3466cd63095d23dfd"}, - {file = "yarl-1.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:77e913b846a6b9c5f767b14dc1e759e5aff05502fe73079f6f4176359d832581"}, - {file = "yarl-1.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0978f29222e649c351b173da2b9b4665ad1feb8d1daa9d971eb90df08702668a"}, - {file = "yarl-1.8.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:388a45dc77198b2460eac0aca1efd6a7c09e976ee768b0d5109173e521a19daf"}, - {file = "yarl-1.8.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2305517e332a862ef75be8fad3606ea10108662bc6fe08509d5ca99503ac2aee"}, - {file = "yarl-1.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42430ff511571940d51e75cf42f1e4dbdded477e71c1b7a17f4da76c1da8ea76"}, - {file = "yarl-1.8.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3150078118f62371375e1e69b13b48288e44f6691c1069340081c3fd12c94d5b"}, - {file = "yarl-1.8.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c15163b6125db87c8f53c98baa5e785782078fbd2dbeaa04c6141935eb6dab7a"}, - {file = "yarl-1.8.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4d04acba75c72e6eb90745447d69f84e6c9056390f7a9724605ca9c56b4afcc6"}, - {file = "yarl-1.8.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e7fd20d6576c10306dea2d6a5765f46f0ac5d6f53436217913e952d19237efc4"}, - {file = "yarl-1.8.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:75c16b2a900b3536dfc7014905a128a2bea8fb01f9ee26d2d7d8db0a08e7cb2c"}, - {file = "yarl-1.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6d88056a04860a98341a0cf53e950e3ac9f4e51d1b6f61a53b0609df342cc8b2"}, - {file = "yarl-1.8.2-cp311-cp311-win32.whl", hash = "sha256:fb742dcdd5eec9f26b61224c23baea46c9055cf16f62475e11b9b15dfd5c117b"}, - {file = "yarl-1.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:8c46d3d89902c393a1d1e243ac847e0442d0196bbd81aecc94fcebbc2fd5857c"}, - {file = "yarl-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ceff9722e0df2e0a9e8a79c610842004fa54e5b309fe6d218e47cd52f791d7ef"}, - {file = "yarl-1.8.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f6b4aca43b602ba0f1459de647af954769919c4714706be36af670a5f44c9c1"}, - {file = "yarl-1.8.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1684a9bd9077e922300ecd48003ddae7a7474e0412bea38d4631443a91d61077"}, - {file = "yarl-1.8.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ebb78745273e51b9832ef90c0898501006670d6e059f2cdb0e999494eb1450c2"}, - {file = "yarl-1.8.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3adeef150d528ded2a8e734ebf9ae2e658f4c49bf413f5f157a470e17a4a2e89"}, - {file = "yarl-1.8.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57a7c87927a468e5a1dc60c17caf9597161d66457a34273ab1760219953f7f4c"}, - {file = "yarl-1.8.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:efff27bd8cbe1f9bd127e7894942ccc20c857aa8b5a0327874f30201e5ce83d0"}, - {file = "yarl-1.8.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a783cd344113cb88c5ff7ca32f1f16532a6f2142185147822187913eb989f739"}, - {file = "yarl-1.8.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:705227dccbe96ab02c7cb2c43e1228e2826e7ead880bb19ec94ef279e9555b5b"}, - {file = "yarl-1.8.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:34c09b43bd538bf6c4b891ecce94b6fa4f1f10663a8d4ca589a079a5018f6ed7"}, - {file = "yarl-1.8.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a48f4f7fea9a51098b02209d90297ac324241bf37ff6be6d2b0149ab2bd51b37"}, - {file = "yarl-1.8.2-cp37-cp37m-win32.whl", hash = "sha256:0414fd91ce0b763d4eadb4456795b307a71524dbacd015c657bb2a39db2eab89"}, - {file = "yarl-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:d881d152ae0007809c2c02e22aa534e702f12071e6b285e90945aa3c376463c5"}, - {file = "yarl-1.8.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5df5e3d04101c1e5c3b1d69710b0574171cc02fddc4b23d1b2813e75f35a30b1"}, - {file = "yarl-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7a66c506ec67eb3159eea5096acd05f5e788ceec7b96087d30c7d2865a243918"}, - {file = "yarl-1.8.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2b4fa2606adf392051d990c3b3877d768771adc3faf2e117b9de7eb977741229"}, - {file = "yarl-1.8.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e21fb44e1eff06dd6ef971d4bdc611807d6bd3691223d9c01a18cec3677939e"}, - {file = "yarl-1.8.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93202666046d9edadfe9f2e7bf5e0782ea0d497b6d63da322e541665d65a044e"}, - {file = "yarl-1.8.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fc77086ce244453e074e445104f0ecb27530d6fd3a46698e33f6c38951d5a0f1"}, - {file = "yarl-1.8.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dd68a92cab699a233641f5929a40f02a4ede8c009068ca8aa1fe87b8c20ae3"}, - {file = "yarl-1.8.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1b372aad2b5f81db66ee7ec085cbad72c4da660d994e8e590c997e9b01e44901"}, - {file = "yarl-1.8.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e6f3515aafe0209dd17fb9bdd3b4e892963370b3de781f53e1746a521fb39fc0"}, - {file = "yarl-1.8.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:dfef7350ee369197106805e193d420b75467b6cceac646ea5ed3049fcc950a05"}, - {file = "yarl-1.8.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:728be34f70a190566d20aa13dc1f01dc44b6aa74580e10a3fb159691bc76909d"}, - {file = "yarl-1.8.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:ff205b58dc2929191f68162633d5e10e8044398d7a45265f90a0f1d51f85f72c"}, - {file = "yarl-1.8.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:baf211dcad448a87a0d9047dc8282d7de59473ade7d7fdf22150b1d23859f946"}, - {file = "yarl-1.8.2-cp38-cp38-win32.whl", hash = "sha256:272b4f1599f1b621bf2aabe4e5b54f39a933971f4e7c9aa311d6d7dc06965165"}, - {file = "yarl-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:326dd1d3caf910cd26a26ccbfb84c03b608ba32499b5d6eeb09252c920bcbe4f"}, - {file = "yarl-1.8.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f8ca8ad414c85bbc50f49c0a106f951613dfa5f948ab69c10ce9b128d368baf8"}, - {file = "yarl-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:418857f837347e8aaef682679f41e36c24250097f9e2f315d39bae3a99a34cbf"}, - {file = "yarl-1.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ae0eec05ab49e91a78700761777f284c2df119376e391db42c38ab46fd662b77"}, - {file = "yarl-1.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:009a028127e0a1755c38b03244c0bea9d5565630db9c4cf9572496e947137a87"}, - {file = "yarl-1.8.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3edac5d74bb3209c418805bda77f973117836e1de7c000e9755e572c1f7850d0"}, - {file = "yarl-1.8.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da65c3f263729e47351261351b8679c6429151ef9649bba08ef2528ff2c423b2"}, - {file = "yarl-1.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ef8fb25e52663a1c85d608f6dd72e19bd390e2ecaf29c17fb08f730226e3a08"}, - {file = "yarl-1.8.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcd7bb1e5c45274af9a1dd7494d3c52b2be5e6bd8d7e49c612705fd45420b12d"}, - {file = "yarl-1.8.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44ceac0450e648de86da8e42674f9b7077d763ea80c8ceb9d1c3e41f0f0a9951"}, - {file = "yarl-1.8.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:97209cc91189b48e7cfe777237c04af8e7cc51eb369004e061809bcdf4e55220"}, - {file = "yarl-1.8.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:48dd18adcf98ea9cd721a25313aef49d70d413a999d7d89df44f469edfb38a06"}, - {file = "yarl-1.8.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e59399dda559688461762800d7fb34d9e8a6a7444fd76ec33220a926c8be1516"}, - {file = "yarl-1.8.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d617c241c8c3ad5c4e78a08429fa49e4b04bedfc507b34b4d8dceb83b4af3588"}, - {file = "yarl-1.8.2-cp39-cp39-win32.whl", hash = "sha256:cb6d48d80a41f68de41212f3dfd1a9d9898d7841c8f7ce6696cf2fd9cb57ef83"}, - {file = "yarl-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:6604711362f2dbf7160df21c416f81fac0de6dbcf0b5445a2ef25478ecc4c778"}, - {file = "yarl-1.8.2.tar.gz", hash = "sha256:49d43402c6e3013ad0978602bf6bf5328535c48d192304b91b97a3c6790b1562"}, -] - -[package.dependencies] -idna = ">=2.0" -multidict = ">=4.0" -typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} - [[package]] name = "zipp" version = "3.11.0" @@ -1910,4 +1347,4 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools" [metadata] lock-version = "2.0" python-versions = ">=3.7,<3.11" -content-hash = "893dd116283c6b9fd4d470d5aea6cb8dbe5c8d0194172f6db67f6639b50d85d9" +content-hash = "8924baccf5bed75b69d29e6157b43f5e90dbe084b73c9fdb695ca0d30d52863b" diff --git a/pyproject.toml b/pyproject.toml index b223a8bc..10616c2c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,12 +13,11 @@ tqdm = "4.56.0" cryptography = "3.4.8" urllib3 = "^1.26.14" requests = "^2.28.2" -pydantic = "^1.9.2" +pydantic-settings = "2.0.1" cffi = "^1.15.1" pre-commit = "^2.19.0" pyinstaller = "^5.4.1" httpx = "^0.23.0" -pilot-platform-common = "^0.1.3" qrcode = "^7.4.2" pytest-click = "^1.1.0" @@ -27,6 +26,7 @@ pytest = "6.2.5" pytest-cov = "^3.0.0" pytest-mock = "^3.7.0" pytest-httpx = "^0.21.2" +pytest-random-order = "1.1.0" requests-mock = "^1.9.3" faker = "18.9.0" @@ -34,6 +34,7 @@ faker = "18.9.0" pilotcli = "app.pilotcli:cli" [tool.pytest.ini_options] +addopts = "-v --random-order" testpaths = "tests" [build-system] diff --git a/tests/app/commands/test_user.py b/tests/app/commands/test_user.py index dfda5a55..70e3bb79 100644 --- a/tests/app/commands/test_user.py +++ b/tests/app/commands/test_user.py @@ -5,19 +5,16 @@ import jwt from app.commands.user import login -from app.configs.app_config import AppConfig from app.configs.user_config import UserConfig def test_login_command_with_api_key_option_calls_keycloak_and_stores_response_in_user_config( - requests_mock, cli_runner, fake + requests_mock, cli_runner, fake, settings ): username = fake.user_name() api_key = fake.pystr(20) access_token = jwt.encode({'preferred_username': username}, key='').decode() - requests_mock.get( - f'{AppConfig.Connections.url_keycloak_realm}/api-key/{api_key}', json={'access_token': access_token} - ) + requests_mock.get(f'{settings.url_keycloak_realm}/api-key/{api_key}', json={'access_token': access_token}) result = cli_runner.invoke(login, ['--api-key', api_key]) diff --git a/tests/app/services/file_manager/file_upload/test_file_upload.py b/tests/app/services/file_manager/file_upload/test_file_upload.py index 726ac845..d7d09174 100644 --- a/tests/app/services/file_manager/file_upload/test_file_upload.py +++ b/tests/app/services/file_manager/file_upload/test_file_upload.py @@ -109,7 +109,7 @@ def test_assemble_path_at_non_existing_folder(mocker): assert create_folder_flag is True -def test_file_upload_skip_empty_file(mocker): +def test_file_upload_skip_empty_file(mocker, tmp_path): file_name = 'test' upload_event = { 'file': file_name, @@ -120,7 +120,7 @@ def test_file_upload_skip_empty_file(mocker): mocker.patch('os.path.isdir', return_value=False) mocker.patch('app.services.file_manager.file_upload.models.FileObject.generate_meta', return_value=(0, 0)) - item_ids = simple_upload(upload_event, output_path='./test') + item_ids = simple_upload(upload_event, output_path=str(tmp_path / 'test')) assert len(item_ids) == 0 diff --git a/tests/app/services/file_manager/file_upload/test_upload_client.py b/tests/app/services/file_manager/file_upload/test_upload_client.py index 9a534b0d..be07b178 100644 --- a/tests/app/services/file_manager/file_upload/test_upload_client.py +++ b/tests/app/services/file_manager/file_upload/test_upload_client.py @@ -153,13 +153,13 @@ def test_resumable_pre_upload_failed_with_404(httpx_mock, mocker): AssertionError('SystemExit not raised') -def test_output_manifest_success(mocker): +def test_output_manifest_success(mocker, tmp_path): upload_client = UploadClient('project_code', 'parent_folder_id') json_dump_mocker = mocker.patch('json.dump', return_value=None) mocker.patch('app.services.file_manager.file_upload.models.FileObject.generate_meta', return_value=(1, 1)) test_obj = FileObject('object/path', 'local_path', 'resumable_id', 'job_id', 'item_id') - res = upload_client.output_manifest([test_obj], 'test') + res = upload_client.output_manifest([test_obj], output_path=str(tmp_path / 'test')) assert res.get('project_code') == 'project_code' assert res.get('parent_folder_id') == 'parent_folder_id' diff --git a/tests/app/services/user_authentication/test_token_manager.py b/tests/app/services/user_authentication/test_token_manager.py index 1b68e8ac..f47e2208 100644 --- a/tests/app/services/user_authentication/test_token_manager.py +++ b/tests/app/services/user_authentication/test_token_manager.py @@ -4,7 +4,6 @@ import jwt -from app.configs.app_config import AppConfig from app.configs.user_config import UserConfig from app.services.user_authentication.token_manager import SrvTokenManager @@ -27,11 +26,11 @@ def test_refresh_calls_refresh_api_key_method_when_is_api_key_method_returns_tru refresh_api_key_mock.assert_called_once() - def test_refresh_api_key_calls_keycloak_and_stores_access_token_in_config(self, requests_mock): + def test_refresh_api_key_calls_keycloak_and_stores_access_token_in_config(self, requests_mock, settings): manager = SrvTokenManager() access_token = jwt.encode({}, key='').decode() requests_mock.get( - f'{AppConfig.Connections.url_keycloak_realm}/api-key/{manager.config.api_key}', + f'{settings.url_keycloak_realm}/api-key/{manager.config.api_key}', json={'access_token': access_token}, ) diff --git a/tests/app/utils/test_aggregated.py b/tests/app/utils/test_aggregated.py index ba736970..6d6e7645 100644 --- a/tests/app/utils/test_aggregated.py +++ b/tests/app/utils/test_aggregated.py @@ -59,6 +59,20 @@ def test_search_file_should_return_200(requests_mock, mocker): assert res['result'] == expected_result +def test_search_item_returns_response_when_status_code_is_404(requests_mock, mocker, fake): + mocker.patch('app.services.user_authentication.token_manager.SrvTokenManager.check_valid', return_value=0) + expected_response = {'result': {'id': fake.uuid4()}} + requests_mock.get( + f'http://bff_cli/v1/project/{test_project_code}/search', + json=expected_response, + status_code=404, + ) + + response = search_item(test_project_code, 'zone', 'folder_relative_path', 'file', 'project') + + assert response == expected_response + + def test_search_file_error_handling_with_403(requests_mock, mocker, capsys): mocker.patch('app.services.user_authentication.token_manager.SrvTokenManager.check_valid', return_value=0) requests_mock.get( @@ -85,5 +99,4 @@ def test_search_file_error_handling_with_401(requests_mock, mocker, capsys): with pytest.raises(SystemExit): search_item(test_project_code, 'zone', 'folder_relative_path', 'file', 'project') out, _ = capsys.readouterr() - out = out.rstrip() - assert out == 'Authentication failed.' + assert out.rstrip() == 'Authentication failed.' diff --git a/tests/conftest.py b/tests/conftest.py index ec2653c9..41dd3eaf 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,6 +7,8 @@ import pytest from app.configs.app_config import AppConfig +from app.configs.config import Settings +from app.configs.config import get_settings from app.configs.user_config import UserConfig from app.models.singleton import Singleton @@ -25,7 +27,6 @@ def mock_settings(monkeypatch, mocker): monkeypatch.setattr(AppConfig.Connections, 'url_download_core', 'http://url_dataset_download_core') monkeypatch.setattr(AppConfig.Connections, 'url_upload_greenroom', 'http://upload_gr') monkeypatch.setattr(AppConfig.Connections, 'url_upload_core', 'http://upload_core') - monkeypatch.setattr(AppConfig.Connections, 'url_keycloak_realm', 'http://keycloak_realm') monkeypatch.setattr(UserConfig, 'username', 'test-user') monkeypatch.setattr(UserConfig, 'password', 'test-password') monkeypatch.setattr(UserConfig, 'api_key', 'test-api-key') @@ -34,6 +35,11 @@ def mock_settings(monkeypatch, mocker): mocker.patch('app.configs.user_config.UserConfig.save') # Do not save config when running tests +@pytest.fixture +def settings() -> Settings: + return get_settings() + + def decoded_token(): current_time = int(time.time()) + 1000 return { @@ -45,7 +51,7 @@ def decoded_token(): 'aud': 'account', 'sub': 'a8b728f6-c95a-4999-b98e-0ccf7492a9b4', 'typ': 'Bearer', - 'azp': AppConfig.Env.keycloak_device_client_id, + 'azp': 'cli', 'nonce': 'a3cb03d0-b00a-480d-8fd2-e06f80898cf1', 'session_state': 'b92a3847-a485-4060-91fd-83300b09acb6', 'acr': '1', From 7ec72bf634bfff73c804510b769638391d84b3c2 Mon Sep 17 00:00:00 2001 From: Color Zhan Date: Tue, 25 Jul 2023 09:27:15 -0400 Subject: [PATCH 11/12] replace all indoc research in license header with indoc systems (#80) Co-authored-by: zhiren --- COPYRIGHT | 4 ++-- app/__init__.py | 4 ++-- app/commands/__init__.py | 4 ++-- app/commands/container_registry.py | 4 ++-- app/commands/dataset.py | 4 ++-- app/commands/entry_point.py | 4 ++-- app/commands/file.py | 4 ++-- app/commands/project.py | 4 ++-- app/commands/user.py | 4 ++-- app/configs/__init__.py | 4 ++-- app/configs/app_config.py | 4 ++-- app/configs/config.py | 4 ++-- app/configs/user_config.py | 4 ++-- app/models/__init__.py | 4 ++-- app/models/enums.py | 4 ++-- app/models/service_meta_class.py | 4 ++-- app/models/singleton.py | 4 ++-- app/models/upload_form.py | 4 ++-- app/pilotcli.py | 4 ++-- app/resources/custom_error.py | 4 ++-- app/resources/custom_help.py | 4 ++-- app/services/__init__.py | 4 ++-- .../container_registry_manager/container_registry_manager.py | 4 ++-- app/services/crypto/__init__.py | 4 ++-- app/services/crypto/crypto.py | 4 ++-- app/services/dataset_manager/dataset_detail.py | 4 ++-- app/services/dataset_manager/dataset_download.py | 4 ++-- app/services/dataset_manager/dataset_list.py | 4 ++-- app/services/dataset_manager/model.py | 4 ++-- app/services/file_manager/__init__.py | 4 ++-- app/services/file_manager/file_download/__init__.py | 4 ++-- app/services/file_manager/file_download/download_client.py | 4 ++-- app/services/file_manager/file_download/model.py | 4 ++-- app/services/file_manager/file_list.py | 4 ++-- app/services/file_manager/file_manifests.py | 4 ++-- app/services/file_manager/file_tag.py | 4 ++-- app/services/file_manager/file_upload/__init__.py | 4 ++-- app/services/file_manager/file_upload/exception.py | 4 ++-- app/services/file_manager/file_upload/file_upload.py | 4 ++-- app/services/file_manager/file_upload/models.py | 4 ++-- app/services/file_manager/file_upload/upload_client.py | 4 ++-- app/services/file_manager/file_upload/upload_validator.py | 4 ++-- app/services/logger_services/__init__.py | 4 ++-- app/services/logger_services/log_functions.py | 4 ++-- app/services/output_manager/__init__.py | 4 ++-- app/services/output_manager/error_handler.py | 4 ++-- app/services/output_manager/help_page.py | 4 ++-- app/services/output_manager/message_handler.py | 4 ++-- app/services/project_manager/__init__.py | 4 ++-- app/services/project_manager/project.py | 4 ++-- app/services/user_authentication/__init__.py | 4 ++-- app/services/user_authentication/decorator.py | 4 ++-- app/services/user_authentication/token_manager.py | 4 ++-- app/services/user_authentication/user_login_logout.py | 4 ++-- app/utils/__init__.py | 4 ++-- app/utils/aggregated.py | 4 ++-- pyproject.toml | 2 +- tests/__init__.py | 4 ++-- tests/app/commands/test_dataset.py | 4 ++-- tests/app/commands/test_file.py | 4 ++-- tests/app/commands/test_user.py | 4 ++-- tests/app/configs/__init__.py | 4 ++-- tests/app/configs/test_user_config.py | 4 ++-- tests/app/services/dataset_manager/test_dataset_detail.py | 4 ++-- tests/app/services/dataset_manager/test_dataset_download.py | 4 ++-- tests/app/services/dataset_manager/test_dataset_list.py | 4 ++-- .../app/services/file_manager/file_upload/test_file_upload.py | 4 ++-- tests/app/services/file_manager/file_upload/test_model.py | 4 ++-- .../services/file_manager/file_upload/test_upload_client.py | 4 ++-- tests/app/services/project_manager/test_project.py | 4 ++-- tests/app/services/user_authentication/test_token_manager.py | 4 ++-- .../services/user_authentication/test_user_login_logout.py | 4 ++-- tests/app/utils/test_aggregated.py | 4 ++-- tests/conftest.py | 4 ++-- tests/fixtures/__init__.py | 4 ++-- tests/fixtures/fake.py | 4 ++-- 76 files changed, 151 insertions(+), 151 deletions(-) diff --git a/COPYRIGHT b/COPYRIGHT index 02313f69..54db7d81 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1,3 +1,3 @@ -Copyright (C) 2022 Indoc Research +Copyright (C) 2022 Indoc Systems -Contact Indoc Research for any questions regarding the use of this source code. +Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/__init__.py b/app/__init__.py index ff372835..96b7c430 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/commands/__init__.py b/app/commands/__init__.py index ff372835..96b7c430 100644 --- a/app/commands/__init__.py +++ b/app/commands/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/commands/container_registry.py b/app/commands/container_registry.py index 1ce1394e..c93296fb 100644 --- a/app/commands/container_registry.py +++ b/app/commands/container_registry.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import click diff --git a/app/commands/dataset.py b/app/commands/dataset.py index 75d1a27d..141e599d 100644 --- a/app/commands/dataset.py +++ b/app/commands/dataset.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import click import questionary diff --git a/app/commands/entry_point.py b/app/commands/entry_point.py index 757e42b7..b7830398 100644 --- a/app/commands/entry_point.py +++ b/app/commands/entry_point.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import os diff --git a/app/commands/file.py b/app/commands/file.py index c5c73177..a77a0dc0 100644 --- a/app/commands/file.py +++ b/app/commands/file.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import json import os diff --git a/app/commands/project.py b/app/commands/project.py index 292b964a..74be4484 100644 --- a/app/commands/project.py +++ b/app/commands/project.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import click import questionary diff --git a/app/commands/user.py b/app/commands/user.py index d7cfca56..94888abe 100644 --- a/app/commands/user.py +++ b/app/commands/user.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. from typing import Union diff --git a/app/configs/__init__.py b/app/configs/__init__.py index ff372835..96b7c430 100644 --- a/app/configs/__init__.py +++ b/app/configs/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/configs/app_config.py b/app/configs/app_config.py index cf72cadf..e3485e0c 100644 --- a/app/configs/app_config.py +++ b/app/configs/app_config.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. from app.configs.config import ConfigClass diff --git a/app/configs/config.py b/app/configs/config.py index ce291796..7b61c438 100644 --- a/app/configs/config.py +++ b/app/configs/config.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. from functools import lru_cache from pathlib import Path diff --git a/app/configs/user_config.py b/app/configs/user_config.py index 3677832a..3b80384f 100644 --- a/app/configs/user_config.py +++ b/app/configs/user_config.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import configparser import os diff --git a/app/models/__init__.py b/app/models/__init__.py index ff372835..96b7c430 100644 --- a/app/models/__init__.py +++ b/app/models/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/models/enums.py b/app/models/enums.py index ee51e7ba..d4f626e4 100644 --- a/app/models/enums.py +++ b/app/models/enums.py @@ -1,6 +1,6 @@ -# Copyright (C) 2023 Indoc Research +# Copyright (C) 2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. from enum import Enum diff --git a/app/models/service_meta_class.py b/app/models/service_meta_class.py index 8557c3f9..e449847a 100644 --- a/app/models/service_meta_class.py +++ b/app/models/service_meta_class.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. class MetaService(type): diff --git a/app/models/singleton.py b/app/models/singleton.py index b1785c8a..c78719e8 100644 --- a/app/models/singleton.py +++ b/app/models/singleton.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. class Singleton(type): diff --git a/app/models/upload_form.py b/app/models/upload_form.py index 70945d4a..2cea1b9f 100644 --- a/app/models/upload_form.py +++ b/app/models/upload_form.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. from app.services.file_manager.file_upload.models import FileObject diff --git a/app/pilotcli.py b/app/pilotcli.py index 872cc559..8d882d8e 100644 --- a/app/pilotcli.py +++ b/app/pilotcli.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import click import requests diff --git a/app/resources/custom_error.py b/app/resources/custom_error.py index 10d594d4..253d8b9c 100644 --- a/app/resources/custom_error.py +++ b/app/resources/custom_error.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. class Error: diff --git a/app/resources/custom_help.py b/app/resources/custom_help.py index 6c154ae5..1b57fe49 100644 --- a/app/resources/custom_help.py +++ b/app/resources/custom_help.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. class HelpPage: diff --git a/app/services/__init__.py b/app/services/__init__.py index ff372835..96b7c430 100644 --- a/app/services/__init__.py +++ b/app/services/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/container_registry_manager/container_registry_manager.py b/app/services/container_registry_manager/container_registry_manager.py index d4053a45..fff0f492 100644 --- a/app/services/container_registry_manager/container_registry_manager.py +++ b/app/services/container_registry_manager/container_registry_manager.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. from json import loads diff --git a/app/services/crypto/__init__.py b/app/services/crypto/__init__.py index ff372835..96b7c430 100644 --- a/app/services/crypto/__init__.py +++ b/app/services/crypto/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/crypto/crypto.py b/app/services/crypto/crypto.py index 026d2d87..40b01b9c 100644 --- a/app/services/crypto/crypto.py +++ b/app/services/crypto/crypto.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import base64 import os diff --git a/app/services/dataset_manager/dataset_detail.py b/app/services/dataset_manager/dataset_detail.py index 0824d32e..daeb72c8 100644 --- a/app/services/dataset_manager/dataset_detail.py +++ b/app/services/dataset_manager/dataset_detail.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import requests diff --git a/app/services/dataset_manager/dataset_download.py b/app/services/dataset_manager/dataset_download.py index 6ee02c64..fdf6ca52 100644 --- a/app/services/dataset_manager/dataset_download.py +++ b/app/services/dataset_manager/dataset_download.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import datetime import os diff --git a/app/services/dataset_manager/dataset_list.py b/app/services/dataset_manager/dataset_list.py index ed69fe85..75b50a2f 100644 --- a/app/services/dataset_manager/dataset_list.py +++ b/app/services/dataset_manager/dataset_list.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. from app.configs.app_config import AppConfig from app.configs.user_config import UserConfig diff --git a/app/services/dataset_manager/model.py b/app/services/dataset_manager/model.py index d57d78cf..b2c3a8c6 100644 --- a/app/services/dataset_manager/model.py +++ b/app/services/dataset_manager/model.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. from enum import Enum diff --git a/app/services/file_manager/__init__.py b/app/services/file_manager/__init__.py index ff372835..96b7c430 100644 --- a/app/services/file_manager/__init__.py +++ b/app/services/file_manager/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/file_manager/file_download/__init__.py b/app/services/file_manager/file_download/__init__.py index ff372835..96b7c430 100644 --- a/app/services/file_manager/file_download/__init__.py +++ b/app/services/file_manager/file_download/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/file_manager/file_download/download_client.py b/app/services/file_manager/file_download/download_client.py index 2586e580..79ed1677 100644 --- a/app/services/file_manager/file_download/download_client.py +++ b/app/services/file_manager/file_download/download_client.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import concurrent.futures import os diff --git a/app/services/file_manager/file_download/model.py b/app/services/file_manager/file_download/model.py index d57d78cf..b2c3a8c6 100644 --- a/app/services/file_manager/file_download/model.py +++ b/app/services/file_manager/file_download/model.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. from enum import Enum diff --git a/app/services/file_manager/file_list.py b/app/services/file_manager/file_list.py index 5abf963c..7c178e5b 100644 --- a/app/services/file_manager/file_list.py +++ b/app/services/file_manager/file_list.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import click import questionary diff --git a/app/services/file_manager/file_manifests.py b/app/services/file_manager/file_manifests.py index e6e98093..72b8cc42 100644 --- a/app/services/file_manager/file_manifests.py +++ b/app/services/file_manager/file_manifests.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import json diff --git a/app/services/file_manager/file_tag.py b/app/services/file_manager/file_tag.py index 91c3e34a..428b3920 100644 --- a/app/services/file_manager/file_tag.py +++ b/app/services/file_manager/file_tag.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import re diff --git a/app/services/file_manager/file_upload/__init__.py b/app/services/file_manager/file_upload/__init__.py index ff372835..96b7c430 100644 --- a/app/services/file_manager/file_upload/__init__.py +++ b/app/services/file_manager/file_upload/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/file_manager/file_upload/exception.py b/app/services/file_manager/file_upload/exception.py index c85c5706..5c7c5b71 100644 --- a/app/services/file_manager/file_upload/exception.py +++ b/app/services/file_manager/file_upload/exception.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. class INVALID_CHUNK_ETAG(Exception): diff --git a/app/services/file_manager/file_upload/file_upload.py b/app/services/file_manager/file_upload/file_upload.py index 1e8814c0..a9000e6d 100644 --- a/app/services/file_manager/file_upload/file_upload.py +++ b/app/services/file_manager/file_upload/file_upload.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import math import os diff --git a/app/services/file_manager/file_upload/models.py b/app/services/file_manager/file_upload/models.py index e722d8e7..fa0875e9 100644 --- a/app/services/file_manager/file_upload/models.py +++ b/app/services/file_manager/file_upload/models.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import math from enum import Enum diff --git a/app/services/file_manager/file_upload/upload_client.py b/app/services/file_manager/file_upload/upload_client.py index 1e1ebbac..0427c796 100644 --- a/app/services/file_manager/file_upload/upload_client.py +++ b/app/services/file_manager/file_upload/upload_client.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import hashlib import json diff --git a/app/services/file_manager/file_upload/upload_validator.py b/app/services/file_manager/file_upload/upload_validator.py index c7b754ad..3e48dcde 100644 --- a/app/services/file_manager/file_upload/upload_validator.py +++ b/app/services/file_manager/file_upload/upload_validator.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import os diff --git a/app/services/logger_services/__init__.py b/app/services/logger_services/__init__.py index ff372835..96b7c430 100644 --- a/app/services/logger_services/__init__.py +++ b/app/services/logger_services/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/logger_services/log_functions.py b/app/services/logger_services/log_functions.py index 036a0bf4..d27af885 100644 --- a/app/services/logger_services/log_functions.py +++ b/app/services/logger_services/log_functions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import click diff --git a/app/services/output_manager/__init__.py b/app/services/output_manager/__init__.py index ff372835..96b7c430 100644 --- a/app/services/output_manager/__init__.py +++ b/app/services/output_manager/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/output_manager/error_handler.py b/app/services/output_manager/error_handler.py index e79ee130..1902d0f7 100644 --- a/app/services/output_manager/error_handler.py +++ b/app/services/output_manager/error_handler.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import enum import sys diff --git a/app/services/output_manager/help_page.py b/app/services/output_manager/help_page.py index 17793d71..d48d0e2b 100644 --- a/app/services/output_manager/help_page.py +++ b/app/services/output_manager/help_page.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import enum diff --git a/app/services/output_manager/message_handler.py b/app/services/output_manager/message_handler.py index 833d8033..0afb6de6 100644 --- a/app/services/output_manager/message_handler.py +++ b/app/services/output_manager/message_handler.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import io diff --git a/app/services/project_manager/__init__.py b/app/services/project_manager/__init__.py index ff372835..96b7c430 100644 --- a/app/services/project_manager/__init__.py +++ b/app/services/project_manager/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/project_manager/project.py b/app/services/project_manager/project.py index f0a7ca20..8a8c930b 100644 --- a/app/services/project_manager/project.py +++ b/app/services/project_manager/project.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import requests diff --git a/app/services/user_authentication/__init__.py b/app/services/user_authentication/__init__.py index ff372835..96b7c430 100644 --- a/app/services/user_authentication/__init__.py +++ b/app/services/user_authentication/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/services/user_authentication/decorator.py b/app/services/user_authentication/decorator.py index a69173e1..9d321060 100644 --- a/app/services/user_authentication/decorator.py +++ b/app/services/user_authentication/decorator.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. from functools import wraps diff --git a/app/services/user_authentication/token_manager.py b/app/services/user_authentication/token_manager.py index 80b4aa0e..ebc574d0 100644 --- a/app/services/user_authentication/token_manager.py +++ b/app/services/user_authentication/token_manager.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import time diff --git a/app/services/user_authentication/user_login_logout.py b/app/services/user_authentication/user_login_logout.py index a66b1ae0..33d71838 100644 --- a/app/services/user_authentication/user_login_logout.py +++ b/app/services/user_authentication/user_login_logout.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import time from typing import Any diff --git a/app/utils/__init__.py b/app/utils/__init__.py index ff372835..96b7c430 100644 --- a/app/utils/__init__.py +++ b/app/utils/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/app/utils/aggregated.py b/app/utils/aggregated.py index 5fcbaab5..440e26e5 100644 --- a/app/utils/aggregated.py +++ b/app/utils/aggregated.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import os import re diff --git a/pyproject.toml b/pyproject.toml index 10616c2c..957a5c54 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ name = "app" version = "2.5.2" description = "This service is designed to support pilot platform" -authors = ["Indoc Research"] +authors = ["Indoc Systems"] [tool.poetry.dependencies] python = ">=3.7,<3.11" diff --git a/tests/__init__.py b/tests/__init__.py index ff372835..96b7c430 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/tests/app/commands/test_dataset.py b/tests/app/commands/test_dataset.py index 76e2ac5e..3d6d5fca 100644 --- a/tests/app/commands/test_dataset.py +++ b/tests/app/commands/test_dataset.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. from app.commands.dataset import dataset_download from app.configs.app_config import AppConfig diff --git a/tests/app/commands/test_file.py b/tests/app/commands/test_file.py index e3aa4721..0e01a49b 100644 --- a/tests/app/commands/test_file.py +++ b/tests/app/commands/test_file.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import click import questionary diff --git a/tests/app/commands/test_user.py b/tests/app/commands/test_user.py index 70e3bb79..3d2ff335 100644 --- a/tests/app/commands/test_user.py +++ b/tests/app/commands/test_user.py @@ -1,6 +1,6 @@ -# Copyright (C) 2023 Indoc Research +# Copyright (C) 2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import jwt diff --git a/tests/app/configs/__init__.py b/tests/app/configs/__init__.py index 950d1b40..cad2bb80 100644 --- a/tests/app/configs/__init__.py +++ b/tests/app/configs/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2023 Indoc Research +# Copyright (C) 2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/tests/app/configs/test_user_config.py b/tests/app/configs/test_user_config.py index 24a483be..ed8c3aeb 100644 --- a/tests/app/configs/test_user_config.py +++ b/tests/app/configs/test_user_config.py @@ -1,6 +1,6 @@ -# Copyright (C) 2023 Indoc Research +# Copyright (C) 2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import os import stat diff --git a/tests/app/services/dataset_manager/test_dataset_detail.py b/tests/app/services/dataset_manager/test_dataset_detail.py index 5628fffa..caaa3459 100644 --- a/tests/app/services/dataset_manager/test_dataset_detail.py +++ b/tests/app/services/dataset_manager/test_dataset_detail.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import pytest diff --git a/tests/app/services/dataset_manager/test_dataset_download.py b/tests/app/services/dataset_manager/test_dataset_download.py index 0afea52e..49537bbe 100644 --- a/tests/app/services/dataset_manager/test_dataset_download.py +++ b/tests/app/services/dataset_manager/test_dataset_download.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. from app.configs.app_config import AppConfig from app.services.dataset_manager.dataset_download import SrvDatasetDownloadManager diff --git a/tests/app/services/dataset_manager/test_dataset_list.py b/tests/app/services/dataset_manager/test_dataset_list.py index 8e010101..43c5be1d 100644 --- a/tests/app/services/dataset_manager/test_dataset_list.py +++ b/tests/app/services/dataset_manager/test_dataset_list.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. from app.services.dataset_manager.dataset_list import SrvDatasetListManager diff --git a/tests/app/services/file_manager/file_upload/test_file_upload.py b/tests/app/services/file_manager/file_upload/test_file_upload.py index d7d09174..b3b4d9b6 100644 --- a/tests/app/services/file_manager/file_upload/test_file_upload.py +++ b/tests/app/services/file_manager/file_upload/test_file_upload.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. from app.configs.app_config import AppConfig from app.services.file_manager.file_upload.file_upload import assemble_path diff --git a/tests/app/services/file_manager/file_upload/test_model.py b/tests/app/services/file_manager/file_upload/test_model.py index 7b9924ab..2a53c17f 100644 --- a/tests/app/services/file_manager/file_upload/test_model.py +++ b/tests/app/services/file_manager/file_upload/test_model.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. from app.configs.app_config import AppConfig from app.services.file_manager.file_upload.models import FileObject diff --git a/tests/app/services/file_manager/file_upload/test_upload_client.py b/tests/app/services/file_manager/file_upload/test_upload_client.py index be07b178..4f7b3bf3 100644 --- a/tests/app/services/file_manager/file_upload/test_upload_client.py +++ b/tests/app/services/file_manager/file_upload/test_upload_client.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import re from functools import wraps diff --git a/tests/app/services/project_manager/test_project.py b/tests/app/services/project_manager/test_project.py index c4d35e61..d7df8fdf 100644 --- a/tests/app/services/project_manager/test_project.py +++ b/tests/app/services/project_manager/test_project.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. from app.services.project_manager.project import SrvProjectManager diff --git a/tests/app/services/user_authentication/test_token_manager.py b/tests/app/services/user_authentication/test_token_manager.py index f47e2208..50379c80 100644 --- a/tests/app/services/user_authentication/test_token_manager.py +++ b/tests/app/services/user_authentication/test_token_manager.py @@ -1,6 +1,6 @@ -# Copyright (C) 2023 Indoc Research +# Copyright (C) 2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import jwt diff --git a/tests/app/services/user_authentication/test_user_login_logout.py b/tests/app/services/user_authentication/test_user_login_logout.py index 33fd5cec..5af15cdf 100644 --- a/tests/app/services/user_authentication/test_user_login_logout.py +++ b/tests/app/services/user_authentication/test_user_login_logout.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import pytest diff --git a/tests/app/utils/test_aggregated.py b/tests/app/utils/test_aggregated.py index 6d6e7645..5617376e 100644 --- a/tests/app/utils/test_aggregated.py +++ b/tests/app/utils/test_aggregated.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import pytest diff --git a/tests/conftest.py b/tests/conftest.py index 41dd3eaf..fcb01dd1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,6 @@ -# Copyright (C) 2022-2023 Indoc Research +# Copyright (C) 2022-2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import time diff --git a/tests/fixtures/__init__.py b/tests/fixtures/__init__.py index 950d1b40..cad2bb80 100644 --- a/tests/fixtures/__init__.py +++ b/tests/fixtures/__init__.py @@ -1,3 +1,3 @@ -# Copyright (C) 2023 Indoc Research +# Copyright (C) 2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. diff --git a/tests/fixtures/fake.py b/tests/fixtures/fake.py index d4bc9933..7db1d77e 100644 --- a/tests/fixtures/fake.py +++ b/tests/fixtures/fake.py @@ -1,6 +1,6 @@ -# Copyright (C) 2023 Indoc Research +# Copyright (C) 2023 Indoc Systems # -# Contact Indoc Research for any questions regarding the use of this source code. +# Contact Indoc Systems for any questions regarding the use of this source code. import faker import pytest From ade326a48df2760ed0bb439d8cb237e8b1870ed3 Mon Sep 17 00:00:00 2001 From: Color Zhan Date: Mon, 31 Jul 2023 09:11:52 -0400 Subject: [PATCH 12/12] Pilot 3376: add more test case with entry point (#82) * add the new test cases for project command * add new test cases for check command entry * update the entry point function with callback * use indoc systems in the license --------- Co-authored-by: zhiren --- app/commands/project.py | 5 +- tests/app/commands/test_entry_point.py | 85 ++++++++++++++++++++++ tests/app/commands/test_project_command.py | 79 ++++++++++++++++++++ tests/conftest.py | 6 ++ 4 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 tests/app/commands/test_entry_point.py create mode 100644 tests/app/commands/test_project_command.py diff --git a/app/commands/project.py b/app/commands/project.py index 74be4484..47b3441c 100644 --- a/app/commands/project.py +++ b/app/commands/project.py @@ -47,12 +47,12 @@ def cli(): ) @doc(project_help.project_help_page(project_help.ProjectHELP.PROJECT_LIST)) def project_list_all(page, page_size, order, order_by, detached): + project_mgr = SrvProjectManager() + if detached: - project_mgr = SrvProjectManager() projects = project_mgr.list_projects(page, page_size, order, order_by) else: while True: - project_mgr = SrvProjectManager() projects = project_mgr.list_projects(page, page_size, order, order_by) if len(projects) < page_size and page == 0: break @@ -62,6 +62,7 @@ def project_list_all(page, page_size, order, order_by, detached): choice = ['next page', 'exit'] else: choice = ['previous page', 'next page', 'exit'] + val = questionary.select('\nWhat do you want?', qmark='', choices=choice).ask() if val == 'exit': mhandler.SrvOutPutHandler.list_success('Project') diff --git a/tests/app/commands/test_entry_point.py b/tests/app/commands/test_entry_point.py new file mode 100644 index 00000000..b95ea560 --- /dev/null +++ b/tests/app/commands/test_entry_point.py @@ -0,0 +1,85 @@ +# Copyright (C) 2022-2023 Indoc Systems +# +# Contact Indoc Systems for any questions regarding the use of this source code. + +from app.commands.dataset import dataset_download +from app.commands.dataset import dataset_list +from app.commands.dataset import dataset_show_detail +from app.commands.entry_point import command_groups +from app.commands.entry_point import entry_point +from app.commands.file import file_check_manifest +from app.commands.file import file_download +from app.commands.file import file_export_manifest +from app.commands.file import file_list +from app.commands.file import file_put +from app.commands.file import file_resume +from app.commands.project import project_list_all +from app.commands.user import login +from app.commands.user import logout + + +def test_entry_point(): + possible_commands = command_groups() + + for x in possible_commands: + assert x in entry_point.commands.keys() + + +def test_project_commands(user_login_true): + assert 'project' in entry_point.commands.keys() + + func_map = { + 'list': project_list_all, + } + project_commands_object = entry_point.commands.get('project') + project_commands_object.callback() + + for x in project_commands_object.commands.keys(): + assert func_map.get(x) == project_commands_object.commands.get(x) + + +def test_user_commands(user_login_true): + assert 'user' in entry_point.commands.keys() + + func_map = { + 'login': login, + 'logout': logout, + } + user_commands_object = entry_point.commands.get('user') + user_commands_object.callback() + + for x in user_commands_object.commands.keys(): + assert func_map.get(x) == user_commands_object.commands.get(x) + + +def test_file_commands(user_login_true): + assert 'file' in entry_point.commands.keys() + + func_map = { + 'list': file_list, + 'upload': file_put, + 'attribute-list': file_check_manifest, + 'attribute-export': file_export_manifest, + 'sync': file_download, + 'resume': file_resume, + } + file_commands_object = entry_point.commands.get('file') + file_commands_object.callback() + + for x in file_commands_object.commands.keys(): + assert func_map.get(x) == file_commands_object.commands.get(x) + + +def test_dataset_commands(user_login_true): + assert 'dataset' in entry_point.commands.keys() + + func_map = { + 'list': dataset_list, + 'show-detail': dataset_show_detail, + 'download': dataset_download, + } + dataset_commands_object = entry_point.commands.get('dataset') + dataset_commands_object.callback() + + for x in dataset_commands_object.commands.keys(): + assert func_map.get(x) == dataset_commands_object.commands.get(x) diff --git a/tests/app/commands/test_project_command.py b/tests/app/commands/test_project_command.py new file mode 100644 index 00000000..d021dd78 --- /dev/null +++ b/tests/app/commands/test_project_command.py @@ -0,0 +1,79 @@ +# Copyright (C) 2022-2023 Indoc Systems +# +# Contact Indoc Systems for any questions regarding the use of this source code. + +from unittest.mock import Mock + +import questionary + +from app.commands.project import project_list_all + + +def list_fake_project(number: int): + return [f'project-{i}' for i in range(number)] + + +def test_list_project_is_smaller_than_page_size(mocker, cli_runner): + page_size = 10 + project_list = list_fake_project(5) + mocker.patch('app.services.project_manager.project.SrvProjectManager.list_projects', return_value=project_list) + + result = cli_runner.invoke( + project_list_all, ['--page', 0, '--page-size', page_size, '--order', 'desc', '--order-by', 'created_at'] + ) + + assert result.exit_code == 0 + assert '' == result.output + + +def test_list_project_is_larger_than_page_size_with_page_0(mocker, cli_runner): + page_size = 10 + project_list = list_fake_project(20) + mocker.patch('app.services.project_manager.project.SrvProjectManager.list_projects', return_value=project_list) + clear_mock = mocker.patch('click.clear', return_value=None) + + question_mock = mocker.patch.object(questionary, 'select', return_value=questionary.select) + questionary.select.return_value.ask = Mock() + questionary.select.return_value.ask.side_effect = ['next page', 'previous page', 'exit'] + + result = cli_runner.invoke( + project_list_all, ['--page', 0, '--page-size', page_size, '--order', 'desc', '--order-by', 'created_at'] + ) + + assert result.exit_code == 0 + assert 'Project list fetched successfully!\n' == result.output + assert question_mock.call_count == 3 + assert clear_mock.call_count == 2 + + +def test_list_project_is_larger_than_page_size_with_page_1(mocker, cli_runner): + page_size = 10 + project_list = list_fake_project(5) + mocker.patch('app.services.project_manager.project.SrvProjectManager.list_projects', return_value=project_list) + clear_mock = mocker.patch('click.clear', return_value=None) + + question_mock = mocker.patch.object(questionary, 'select', return_value=questionary.select) + questionary.select.return_value.ask = Mock() + questionary.select.return_value.ask.side_effect = ['previous page', 'exit'] + + result = cli_runner.invoke( + project_list_all, ['--page', 1, '--page-size', page_size, '--order', 'desc', '--order-by', 'created_at'] + ) + + assert result.exit_code == 0 + assert '' == result.output + assert question_mock.call_count == 1 + assert clear_mock.call_count == 1 + + +def test_list_project_with_detached(mocker, cli_runner): + page_size = 10 + project_list = list_fake_project(10) + mocker.patch('app.services.project_manager.project.SrvProjectManager.list_projects', return_value=project_list) + + result = cli_runner.invoke( + project_list_all, + ['--page', 0, '--page-size', page_size, '--order', 'desc', '--order-by', 'created_at', '--detached'], + ) + + assert result.exit_code == 0 diff --git a/tests/conftest.py b/tests/conftest.py index fcb01dd1..8d85ac44 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -35,6 +35,12 @@ def mock_settings(monkeypatch, mocker): mocker.patch('app.configs.user_config.UserConfig.save') # Do not save config when running tests +@pytest.fixture +def user_login_true(mocker): + mocker.patch('app.services.user_authentication.decorator.check_is_login', return_value=True) + mocker.patch('app.services.user_authentication.decorator.check_is_active', return_value=True) + + @pytest.fixture def settings() -> Settings: return get_settings()